· 8 years ago · Feb 26, 2018, 07:12 AM
1diff --git a/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Price.php b/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Price.php
2index 4cf4e15b6c0..64636c8469e 100644
3--- a/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Price.php
4+++ b/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Price.php
5@@ -19,20 +19,11 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D
6 *
7 * @return $this
8 * @throws \Exception
9+ * @deprecated
10 */
11 public function reindexAll()
12 {
13- $this->tableStrategy->setUseIdxTable(true);
14-
15- $this->beginTransaction();
16- try {
17- $this->_prepareBundlePrice();
18- $this->commit();
19- } catch (\Exception $e) {
20- $this->rollBack();
21- throw $e;
22- }
23-
24+ parent::reindexAll();
25 return $this;
26 }
27
28@@ -41,13 +32,20 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D
29 *
30 * @param int|array $entityIds
31 * @return $this
32+ * @deprecated
33 */
34 public function reindexEntity($entityIds)
35 {
36- $this->_prepareBundlePrice($entityIds);
37-
38+ parent::reindexEntity($entityIds);
39 return $this;
40 }
41+ /**
42+ * @inheritdoc
43+ */
44+ protected function reindex($entityIds = null)
45+ {
46+ $this->_prepareBundlePrice($entityIds);
47+ }
48
49 /**
50 * Retrieve temporary price index table name for fixed bundle products
51diff --git a/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Stock.php b/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Stock.php
52index c05c1dc8b7f..c84e2963e13 100644
53--- a/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Stock.php
54+++ b/vendor/magento/module-bundle/Model/ResourceModel/Indexer/Stock.php
55@@ -6,6 +6,8 @@
56 namespace Magento\Bundle\Model\ResourceModel\Indexer;
57
58 use Magento\Catalog\Api\Data\ProductInterface;
59+use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
60+use Magento\Framework\App\ObjectManager;
61
62 /**
63 * Bundle Stock Status Indexer Resource Model
64@@ -14,16 +16,44 @@ use Magento\Catalog\Api\Data\ProductInterface;
65 */
66 class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock
67 {
68+ /**
69+ * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
70+ */
71+ private $activeTableSwitcher;
72+
73+ /**
74+ * Stock constructor.
75+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
76+ * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
77+ * @param \Magento\Eav\Model\Config $eavConfig
78+ * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
79+ * @param null $connectionName
80+ * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
81+ */
82+ public function __construct(
83+ \Magento\Framework\Model\ResourceModel\Db\Context $context,
84+ \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
85+ \Magento\Eav\Model\Config $eavConfig,
86+ \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
87+ $connectionName = null,
88+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null
89+ ) {
90+ parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName);
91+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get(
92+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class
93+ );
94+ }
95+
96 /**
97 * Reindex temporary (price result data) for defined product(s)
98 *
99 * @param int|array $entityIds
100 * @return $this
101+ * @deprecated
102 */
103 public function reindexEntity($entityIds)
104 {
105- $this->_updateIndex($entityIds);
106-
107+ parent::reindexEntity($entityIds);
108 return $this;
109 }
110
111@@ -48,7 +78,10 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\
112 {
113 $this->_cleanBundleOptionStockData();
114 $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
115- $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable();
116+ $table = $this->getActionType() === Full::ACTION_TYPE
117+ ? $this->activeTableSwitcher->getAdditionalTableName($this->getMainTable())
118+ : $this->getMainTable();
119+ $idxTable = $usePrimaryTable ? $table : $this->getIdxTable();
120 $connection = $this->getConnection();
121 $select = $connection->select()->from(
122 ['product' => $this->getTable('catalog_product_entity')],
123diff --git a/vendor/magento/module-bundle/etc/di.xml b/vendor/magento/module-bundle/etc/di.xml
124index 2899ce28d04..64ed00121e6 100644
125--- a/vendor/magento/module-bundle/etc/di.xml
126+++ b/vendor/magento/module-bundle/etc/di.xml
127@@ -129,4 +129,24 @@
128 </argument>
129 </arguments>
130 </type>
131+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator">
132+ <arguments>
133+ <argument name="estimators" xsi:type="array">
134+ <item name="bundle" xsi:type="object">Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement</item>
135+ </argument>
136+ </arguments>
137+ </type>
138+ <type name="Magento\Bundle\Model\ResourceModel\Indexer\Price">
139+ <arguments>
140+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
141+ <argument name="connectionName" xsi:type="string">indexer</argument>
142+ </arguments>
143+ </type>
144+ <type name="Magento\CatalogInventory\Model\Indexer\Stock\Action\Full">
145+ <arguments>
146+ <argument name="batchRowsCount" xsi:type="array">
147+ <item name="bundle" xsi:type="number">136</item>
148+ </argument>
149+ </arguments>
150+ </type>
151 </config>
152diff --git a/vendor/magento/module-catalog/Model/Indexer/Category/Product/AbstractAction.php b/vendor/magento/module-catalog/Model/Indexer/Category/Product/AbstractAction.php
153index 250ea1d477a..013a6e4016f 100644
154--- a/vendor/magento/module-catalog/Model/Indexer/Category/Product/AbstractAction.php
155+++ b/vendor/magento/module-catalog/Model/Indexer/Category/Product/AbstractAction.php
156@@ -165,6 +165,9 @@ abstract class AbstractAction
157 /**
158 * Return main index table name
159 *
160+ * This table should be used on frontend(clients)
161+ * The name is switched between 'catalog_category_product_index' and 'catalog_category_product_index_replica'
162+ *
163 * @return string
164 */
165 protected function getMainTable()
166diff --git a/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Full.php b/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Full.php
167index 5a5dced86f9..d4ee1d66757 100644
168--- a/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Full.php
169+++ b/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Full.php
170@@ -5,6 +5,22 @@
171 */
172 namespace Magento\Catalog\Model\Indexer\Category\Product\Action;
173
174+use Magento\Framework\App\ObjectManager;
175+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
176+use Magento\Framework\DB\Query\Generator as QueryGenerator;
177+use Magento\Framework\App\ResourceConnection;
178+use Magento\Store\Model\StoreManagerInterface;
179+use Magento\Catalog\Model\Config;
180+use Magento\Framework\Indexer\BatchProviderInterface;
181+use Magento\Framework\Indexer\BatchSizeManagementInterface;
182+use Magento\Framework\EntityManager\MetadataPool;
183+
184+/**
185+ * Class Full reindex action
186+ *
187+ * @package Magento\Catalog\Model\Indexer\Category\Product\Action
188+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
189+ */
190 class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction
191 {
192 /**
193@@ -14,6 +30,68 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio
194 */
195 protected $useTempTable = false;
196
197+ /**
198+ * @var \Magento\Framework\Indexer\BatchSizeManagementInterface
199+ */
200+ private $batchSizeManagement;
201+
202+ /**
203+ * @var \Magento\Framework\Indexer\BatchProviderInterface
204+ */
205+ private $batchProvider;
206+
207+ /**
208+ * @var \Magento\Framework\EntityManager\MetadataPool
209+ */
210+ protected $metadataPool;
211+
212+ /**
213+ * Row count to process in a batch
214+ *
215+ * @var int
216+ */
217+ private $batchRowsCount;
218+
219+ /**
220+ * @var ActiveTableSwitcher
221+ */
222+ private $activeTableSwitcher;
223+
224+ /**
225+ * @param ResourceConnection $resource
226+ * @param \Magento\Store\Model\StoreManagerInterface $storeManager
227+ * @param \Magento\Catalog\Model\Config $config
228+ * @param QueryGenerator|null $queryGenerator
229+ * @param \Magento\Framework\Indexer\BatchSizeManagementInterface|null $batchSizeManagement
230+ * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider
231+ * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool
232+ * @param int|null $batchRowsCount
233+ * @param ActiveTableSwitcher|null $activeTableSwitcher
234+ */
235+ public function __construct(
236+ ResourceConnection $resource,
237+ StoreManagerInterface $storeManager,
238+ Config $config,
239+ QueryGenerator $queryGenerator = null,
240+ BatchSizeManagementInterface $batchSizeManagement = null,
241+ BatchProviderInterface $batchProvider = null,
242+ MetadataPool $metadataPool = null,
243+ $batchRowsCount = null,
244+ ActiveTableSwitcher $activeTableSwitcher = null
245+ ) {
246+ parent::__construct(
247+ $resource,
248+ $storeManager,
249+ $config
250+ );
251+ $objectManager = ObjectManager::getInstance();
252+ $this->batchSizeManagement = $batchSizeManagement ?: $objectManager->get(BatchSizeManagementInterface::class);
253+ $this->batchProvider = $batchProvider ?: $objectManager->get(BatchProviderInterface::class);
254+ $this->metadataPool = $metadataPool ?: $objectManager->get(MetadataPool::class);
255+ $this->batchRowsCount = $batchRowsCount;
256+ $this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class);
257+ }
258+
259 /**
260 * Refresh entities index
261 *
262@@ -21,13 +99,8 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio
263 */
264 public function execute()
265 {
266- $this->clearTmpData();
267-
268 $this->reindex();
269-
270- $this->publishData();
271- $this->removeUnnecessaryData();
272-
273+ $this->activeTableSwitcher->switchTable($this->connection, [$this->getMainTable()]);
274 return $this;
275 }
276
277@@ -42,7 +115,7 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio
278 $this->getMainTable(),
279 []
280 )->joinLeft(
281- ['t' => $this->getMainTmpTable()],
282+ ['t' => $this->getMainTable()],
283 $this->getMainTable() .
284 '.category_id = t.category_id AND ' .
285 $this->getMainTable() .
286@@ -75,19 +148,17 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio
287 protected function publishData()
288 {
289 $select = $this->connection->select()->from($this->getMainTmpTable());
290+ $columns = array_keys($this->connection->describeTable($this->getMainTable()));
291+ $tableName = $this->activeTableSwitcher->getAdditionalTableName($this->getMainTable());
292
293- $queries = $this->prepareSelectsByRange($select, 'category_id');
294-
295- foreach ($queries as $query) {
296- $this->connection->query(
297- $this->connection->insertFromSelect(
298- $query,
299- $this->getMainTable(),
300- ['category_id', 'product_id', 'position', 'is_parent', 'store_id', 'visibility'],
301- \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE
302- )
303- );
304- }
305+ $this->connection->query(
306+ $this->connection->insertFromSelect(
307+ $select,
308+ $tableName,
309+ $columns,
310+ \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE
311+ )
312+ );
313 }
314
315 /**
316@@ -99,4 +170,75 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio
317 {
318 $this->connection->delete($this->getMainTmpTable());
319 }
320+
321+ /**
322+ * {@inheritdoc}
323+ */
324+ protected function reindexRootCategory(\Magento\Store\Model\Store $store)
325+ {
326+ if ($this->isIndexRootCategoryNeeded()) {
327+ $this->reindexCategoriesBySelect($this->getAllProducts($store), 'cp.entity_id IN (?)');
328+ }
329+ }
330+
331+ /**
332+ * Reindex products of anchor categories
333+ *
334+ * @param \Magento\Store\Model\Store $store
335+ * @return void
336+ */
337+ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store)
338+ {
339+ $this->reindexCategoriesBySelect($this->getAnchorCategoriesSelect($store), 'ccp.product_id IN (?)');
340+ }
341+
342+ /**
343+ * Reindex products of non anchor categories
344+ *
345+ * @param \Magento\Store\Model\Store $store
346+ * @return void
347+ */
348+ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store)
349+ {
350+ $this->reindexCategoriesBySelect($this->getNonAnchorCategoriesSelect($store), 'ccp.product_id IN (?)');
351+ }
352+
353+ /**
354+ * Reindex categories using given SQL select and condition.
355+ *
356+ * @param \Magento\Framework\DB\Select $basicSelect
357+ * @param string $whereCondition
358+ * @return void
359+ */
360+ private function reindexCategoriesBySelect(\Magento\Framework\DB\Select $basicSelect, $whereCondition)
361+ {
362+ $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
363+ $columns = array_keys($this->connection->describeTable($this->getMainTmpTable()));
364+ $this->batchSizeManagement->ensureBatchSize($this->connection, $this->batchRowsCount);
365+ $batches = $this->batchProvider->getBatches(
366+ $this->connection,
367+ $entityMetadata->getEntityTable(),
368+ $entityMetadata->getIdentifierField(),
369+ $this->batchRowsCount
370+ );
371+ foreach ($batches as $batch) {
372+ $this->clearTmpData();
373+ $resultSelect = clone $basicSelect;
374+ $select = $this->connection->select();
375+ $select->distinct(true);
376+ $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField());
377+ $entityIds = $this->batchProvider->getBatchIds($this->connection, $select, $batch);
378+ $resultSelect->where($whereCondition, $entityIds);
379+ $this->connection->query(
380+ $this->connection->insertFromSelect(
381+ $resultSelect,
382+ $this->getMainTmpTable(),
383+ $columns,
384+ \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE
385+ )
386+ );
387+ $this->publishData();
388+ $this->removeUnnecessaryData();
389+ }
390+ }
391 }
392diff --git a/vendor/magento/module-catalog/Model/Indexer/Category/Product/RowSizeEstimator.php b/vendor/magento/module-catalog/Model/Indexer/Category/Product/RowSizeEstimator.php
393new file mode 100644
394index 00000000000..7057d1c0667
395--- /dev/null
396+++ b/vendor/magento/module-catalog/Model/Indexer/Category/Product/RowSizeEstimator.php
397@@ -0,0 +1,77 @@
398+<?php
399+/**
400+ * Copyright © Magento, Inc. All rights reserved.
401+ * See COPYING.txt for license details.
402+ */
403+
404+namespace Magento\Catalog\Model\Indexer\Category\Product;
405+
406+use Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface;
407+
408+/**
409+ * Class RowSizeEstimator
410+ * Intended to estimate amount of memory necessary for saving the biggest category in the DB
411+ * @package Magento\Catalog\Model\Indexer\Category\Product
412+ */
413+class RowSizeEstimator implements IndexTableRowSizeEstimatorInterface
414+{
415+ /**
416+ * Amount of memory for index data row.
417+ */
418+ const ROW_MEMORY_SIZE = 100;
419+
420+ /**
421+ * @var \Magento\Framework\App\ResourceConnection
422+ */
423+ private $resourceConnection;
424+
425+ /**
426+ * @param \Magento\Framework\App\ResourceConnection $resourceConnection
427+ */
428+ public function __construct(
429+ \Magento\Framework\App\ResourceConnection $resourceConnection
430+ ) {
431+ $this->resourceConnection = $resourceConnection;
432+ }
433+
434+ /**
435+ * Calculate memory size for largest possible category in database.
436+ *
437+ * Result value is a multiplication of
438+ * a) maximum amount of products in one category
439+ * b) amount of store groups
440+ * c) memory amount per each index row in DB table
441+ *
442+ * {@inheritdoc}
443+ */
444+ public function estimateRowSize()
445+ {
446+ $connection = $this->resourceConnection->getConnection();
447+
448+ // get store groups count except the default
449+ $storeGroupSelect = $connection->select()
450+ ->from(
451+ $this->resourceConnection->getTableName('store_group'),
452+ ['count' => new \Zend_Db_Expr('count(*)')]
453+ )->where('group_id > 0');
454+ $storeGroupCount = $connection->fetchOne($storeGroupSelect);
455+
456+ // get max possible categories per product
457+ // subselect with categories count per product
458+ $categoryCounterSubSelect = $connection->select()
459+ ->from(
460+ $this->resourceConnection->getTableName('catalog_category_product'),
461+ ['counter' => new \Zend_Db_Expr('count(category_id)')]
462+ )->group('product_id');
463+
464+ // select maximum value from subselect
465+ $productCountSelect = $connection->select()
466+ ->from(
467+ ['counters' => $categoryCounterSubSelect],
468+ [new \Zend_Db_Expr('max(counter)')]
469+ );
470+ $maxProducts = $connection->fetchOne($productCountSelect);
471+
472+ return ceil($storeGroupCount * $maxProducts * self::ROW_MEMORY_SIZE);
473+ }
474+}
475diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Eav/AbstractAction.php b/vendor/magento/module-catalog/Model/Indexer/Product/Eav/AbstractAction.php
476index bd3586eff92..ce31c4f2182 100644
477--- a/vendor/magento/module-catalog/Model/Indexer/Product/Eav/AbstractAction.php
478+++ b/vendor/magento/module-catalog/Model/Indexer/Product/Eav/AbstractAction.php
479@@ -28,6 +28,7 @@ abstract class AbstractAction
480 protected $_eavDecimalFactory;
481
482 /**
483+ * AbstractAction constructor.
484 * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory
485 * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory
486 */
487@@ -84,6 +85,7 @@ abstract class AbstractAction
488 * Reindex entities
489 *
490 * @param null|array|int $ids
491+ * @throws \Exception
492 * @return void
493 */
494 public function reindex($ids = null)
495@@ -92,8 +94,55 @@ abstract class AbstractAction
496 if ($ids === null) {
497 $indexer->reindexAll();
498 } else {
499+ if (!is_array($ids)) {
500+ $ids = [$ids];
501+ }
502+ $ids = $this->processRelations($indexer, $ids);
503 $indexer->reindexEntities($ids);
504+ $destinationTable = $indexer->getMainTable();
505+ $this->syncData($indexer, $destinationTable, $ids);
506 }
507 }
508 }
509+
510+ /**
511+ * Synchronize data between index storage and original storage
512+ *
513+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav $indexer
514+ * @param string $destinationTable
515+ * @param array $ids
516+ * @throws \Exception
517+ * @return void
518+ */
519+ protected function syncData($indexer, $destinationTable, $ids)
520+ {
521+ $connection = $indexer->getConnection();
522+ $connection->beginTransaction();
523+ try {
524+ // remove old index
525+ $where = $connection->quoteInto('entity_id IN(?)', $ids);
526+ $connection->delete($destinationTable, $where);
527+ // insert new index
528+ $indexer->insertFromTable($indexer->getIdxTable(), $destinationTable);
529+ $connection->commit();
530+ } catch (\Exception $e) {
531+ $connection->rollBack();
532+ throw $e;
533+ }
534+ }
535+
536+ /**
537+ * Retrieve product relations by children and parent
538+ *
539+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav $indexer
540+ * @param array $ids
541+ *
542+ * @return $ids
543+ */
544+ protected function processRelations($indexer, $ids)
545+ {
546+ $parentIds = $indexer->getRelationsByChild($ids);
547+ $childIds = $indexer->getRelationsByParent($ids);
548+ return array_unique(array_merge($ids, $childIds, $parentIds));
549+ }
550 }
551diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Eav/Action/Full.php b/vendor/magento/module-catalog/Model/Indexer/Product/Eav/Action/Full.php
552index 7c2b2c137da..0c41c3e6eb0 100644
553--- a/vendor/magento/module-catalog/Model/Indexer/Product/Eav/Action/Full.php
554+++ b/vendor/magento/module-catalog/Model/Indexer/Product/Eav/Action/Full.php
555@@ -5,11 +5,66 @@
556 */
557 namespace Magento\Catalog\Model\Indexer\Product\Eav\Action;
558
559+use Magento\Framework\App\ObjectManager;
560+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory;
561+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory;
562+use Magento\Framework\EntityManager\MetadataPool;
563+use Magento\Framework\Indexer\BatchProviderInterface;
564+use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator;
565+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
566+use Magento\Catalog\Api\Data\ProductInterface;
567+use Magento\Framework\Exception\LocalizedException;
568+use Magento\Framework\DB\Adapter\AdapterInterface;
569+
570 /**
571 * Class Full reindex action
572 */
573 class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction
574 {
575+ /**
576+ * @var \Magento\Framework\EntityManager\MetadataPool
577+ */
578+ private $metadataPool;
579+
580+ /**
581+ * @var \Magento\Framework\Indexer\BatchProviderInterface
582+ */
583+ private $batchProvider;
584+
585+ /**
586+ * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator
587+ */
588+ private $batchSizeCalculator;
589+
590+ /**
591+ * @var ActiveTableSwitcher
592+ */
593+ private $activeTableSwitcher;
594+
595+ /**
596+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory
597+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory
598+ * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool
599+ * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider
600+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator
601+ * @param ActiveTableSwitcher|null $activeTableSwitcher
602+ */
603+ public function __construct(
604+ DecimalFactory $eavDecimalFactory,
605+ SourceFactory $eavSourceFactory,
606+ MetadataPool $metadataPool = null,
607+ BatchProviderInterface $batchProvider = null,
608+ BatchSizeCalculator $batchSizeCalculator = null,
609+ ActiveTableSwitcher $activeTableSwitcher = null
610+ ) {
611+ parent::__construct($eavDecimalFactory, $eavSourceFactory);
612+ $objectManager = ObjectManager::getInstance();
613+ $this->metadataPool = $metadataPool ?: $objectManager->get(MetadataPool::class);
614+ $this->batchProvider = $batchProvider ?: $objectManager->get(BatchProviderInterface::class);
615+ $this->batchSizeCalculator = $batchSizeCalculator ?: $objectManager->get(BatchSizeCalculator::class);
616+ $this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class);
617+ }
618+
619 /**
620 * Execute Full reindex
621 *
622@@ -21,9 +76,59 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction
623 public function execute($ids = null)
624 {
625 try {
626- $this->reindex();
627+ foreach ($this->getIndexers() as $indexerName => $indexer) {
628+ $connection = $indexer->getConnection();
629+ $mainTable = $this->activeTableSwitcher->getAdditionalTableName($indexer->getMainTable());
630+ $connection->truncateTable($mainTable);
631+ $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class);
632+ $batches = $this->batchProvider->getBatches(
633+ $connection,
634+ $entityMetadata->getEntityTable(),
635+ $entityMetadata->getIdentifierField(),
636+ $this->batchSizeCalculator->estimateBatchSize($connection, $indexerName)
637+ );
638+
639+ foreach ($batches as $batch) {
640+ /** @var \Magento\Framework\DB\Select $select */
641+ $select = $connection->select();
642+ $select->distinct(true);
643+ $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField());
644+ $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch);
645+ if (!empty($entityIds)) {
646+ $indexer->reindexEntities($this->processRelations($indexer, $entityIds));
647+ $this->syncData($indexer, $mainTable);
648+ }
649+ }
650+ $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]);
651+ }
652+ } catch (\Exception $e) {
653+ throw new LocalizedException(__($e->getMessage()), $e);
654+ }
655+ }
656+
657+ /**
658+ * @inheritdoc
659+ */
660+ protected function syncData($indexer, $destinationTable, $ids = null)
661+ {
662+ $connection = $indexer->getConnection();
663+ $connection->beginTransaction();
664+ try {
665+ $sourceTable = $indexer->getIdxTable();
666+ $sourceColumns = array_keys($connection->describeTable($sourceTable));
667+ $targetColumns = array_keys($connection->describeTable($destinationTable));
668+ $select = $connection->select()->from($sourceTable, $sourceColumns);
669+ $query = $connection->insertFromSelect(
670+ $select,
671+ $destinationTable,
672+ $targetColumns,
673+ AdapterInterface::INSERT_ON_DUPLICATE
674+ );
675+ $connection->query($query);
676+ $connection->commit();
677 } catch (\Exception $e) {
678- throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
679+ $connection->rollBack();
680+ throw $e;
681 }
682 }
683 }
684diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Price/AbstractAction.php b/vendor/magento/module-catalog/Model/Indexer/Product/Price/AbstractAction.php
685index 3b3804220db..b41de15063e 100644
686--- a/vendor/magento/module-catalog/Model/Indexer/Product/Price/AbstractAction.php
687+++ b/vendor/magento/module-catalog/Model/Indexer/Product/Price/AbstractAction.php
688@@ -70,6 +70,11 @@ abstract class AbstractAction
689 */
690 protected $_indexers;
691
692+ /**
693+ * @var \Magento\Catalog\Model\ResourceModel\Product
694+ */
695+ private $productResource;
696+
697 /**
698 * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
699 * @param \Magento\Store\Model\StoreManagerInterface $storeManager
700@@ -213,46 +218,80 @@ abstract class AbstractAction
701 $table = $this->_defaultIndexerResource->getTable('catalog_product_index_tier_price');
702 $this->_emptyTable($table);
703
704- $websiteExpression = $this->_connection->getCheckSql(
705- 'tp.website_id = 0',
706- 'ROUND(tp.value * cwd.rate, 4)',
707- 'tp.value'
708- );
709 $linkField = $this->getProductIdFieldName();
710- $select = $this->_connection->select()->from(
711+ $priceAttribute = $this->getProductResource()->getAttribute('price');
712+ $baseColumns = [
713+ 'cpe.entity_id',
714+ 'tp.customer_group_id',
715+ 'tp.website_id'
716+ ];
717+ if ($linkField !== 'entity_id') {
718+ $baseColumns[] = 'cpe.' . $linkField;
719+ };
720+ $subSelect = $this->_connection->select()->from(
721 ['cpe' => $this->_defaultIndexerResource->getTable('catalog_product_entity')],
722- ['cpe.entity_id']
723- )->join(
724+ array_merge_recursive(
725+ $baseColumns,
726+ ['value' => 'min(tp.value)']
727+ )
728+ )->joinInner(
729 ['tp' => $this->_defaultIndexerResource->getTable(['catalog_product_entity', 'tier_price'])],
730 'tp.' . $linkField . ' = cpe.' . $linkField,
731 []
732- )->join(
733- ['cg' => $this->_defaultIndexerResource->getTable('customer_group')],
734- 'tp.all_groups = 1 OR (tp.all_groups = 0 AND tp.customer_group_id = cg.customer_group_id)',
735- ['customer_group_id']
736- )->join(
737- ['cw' => $this->_defaultIndexerResource->getTable('store_website')],
738- 'tp.website_id = 0 OR tp.website_id = cw.website_id',
739- ['website_id']
740- )->join(
741- ['cwd' => $this->_defaultIndexerResource->getTable('catalog_product_index_website')],
742- 'cw.website_id = cwd.website_id',
743- []
744- )->where(
745- 'cw.website_id != 0'
746- )->columns(
747- new \Zend_Db_Expr("MIN({$websiteExpression})")
748- )->group(
749- ['cpe.entity_id', 'cg.customer_group_id', 'cw.website_id']
750- );
751+ )->where("cpe.entity_id IN(?)", $entityIds)
752+ ->where("tp.website_id != 0")
753+ ->group(['cpe.entity_id', 'tp.customer_group_id', 'tp.website_id']);
754+
755+ $subSelect2 = $this->_connection->select()
756+ ->from(
757+ ['cpe' => $this->_defaultIndexerResource->getTable('catalog_product_entity')],
758+ array_merge_recursive(
759+ $baseColumns,
760+ ['MIN(ROUND(tp.value * cwd.rate, 4)) AS value']
761+ )
762+ )
763+ ->joinInner(
764+ ['tp' => $this->_defaultIndexerResource->getTable(['catalog_product_entity', 'tier_price'])],
765+ 'tp.' . $linkField . ' = cpe.' . $linkField,
766+ []
767+ )->join(
768+ ['cw' => $this->_defaultIndexerResource->getTable('store_website')],
769+ true,
770+ []
771+ )
772+ ->joinInner(
773+ ['cwd' => $this->_defaultIndexerResource->getTable('catalog_product_index_website')],
774+ 'cw.website_id = cwd.website_id',
775+ []
776+ )
777+ ->where("cpe.entity_id IN(?)", $entityIds)
778+ ->where("tp.website_id = 0")
779+ ->group(
780+ ['cpe.entity_id', 'tp.customer_group_id', 'tp.website_id']
781+ );
782
783- if (!empty($entityIds)) {
784- $select->where("cpe.entity_id IN(?)", $entityIds);
785- }
786+ $unionSelect = $this->_connection->select()
787+ ->union([$subSelect, $subSelect2], \Magento\Framework\DB\Select::SQL_UNION_ALL);
788+ $select = $this->_connection->select()
789+ ->from(
790+ ['b' => new \Zend_Db_Expr(sprintf('(%s)', $unionSelect->assemble()))],
791+ [
792+ 'b.entity_id',
793+ 'b.customer_group_id',
794+ 'b.website_id',
795+ 'b.value'
796+ ]
797+ )
798+ ->joinInner(
799+ ['product_price' => $priceAttribute->getBackend()->getTable()],
800+ 'b.' . $linkField . ' = product_price.' . $linkField,
801+ []
802+ )
803+ ->group(['b.entity_id', 'b.customer_group_id', 'b.website_id']);
804+
805+ $query = $select->insertFromSelect($table, [], false);
806
807- $query = $select->insertFromSelect($table);
808 $this->_connection->query($query);
809-
810 return $this;
811 }
812
813@@ -441,7 +480,7 @@ abstract class AbstractAction
814
815 if ($children) {
816 $select = $this->_connection->select()->from(
817- $this->_defaultIndexerResource->getTable('catalog_product_index_price')
818+ $this->getIndexTargetTable()
819 )->where(
820 'entity_id IN(?)',
821 $children
822@@ -453,6 +492,18 @@ abstract class AbstractAction
823 return $this;
824 }
825
826+ /**
827+ * Retrieve index table that will be used for write operations.
828+ *
829+ * This method is used during both partial and full reindex to identify the table.
830+ *
831+ * @return string
832+ */
833+ protected function getIndexTargetTable()
834+ {
835+ return $this->_defaultIndexerResource->getTable('catalog_product_index_price');
836+ }
837+
838 /**
839 * @return string
840 */
841@@ -462,4 +513,17 @@ abstract class AbstractAction
842 $indexList = $this->_connection->getIndexList($table);
843 return $indexList[$this->_connection->getPrimaryKeyName($table)]['COLUMNS_LIST'][0];
844 }
845+
846+ /**
847+ * @return \Magento\Catalog\Model\ResourceModel\Product
848+ * @deprecated
849+ */
850+ private function getProductResource()
851+ {
852+ if (null === $this->productResource) {
853+ $this->productResource = \Magento\Framework\App\ObjectManager::getInstance()
854+ ->get(\Magento\Catalog\Model\ResourceModel\Product::class);
855+ }
856+ return $this->productResource;
857+ }
858 }
859diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Full.php b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Full.php
860index 8148703b177..ed43c243f35 100644
861--- a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Full.php
862+++ b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Full.php
863@@ -5,33 +5,163 @@
864 */
865 namespace Magento\Catalog\Model\Indexer\Product\Price\Action;
866
867+use Magento\Framework\App\ObjectManager;
868+
869 /**
870 * Class Full reindex action
871- *
872+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
873 */
874 class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction
875 {
876+ /**
877+ * @var \Magento\Framework\EntityManager\MetadataPool
878+ */
879+ private $metadataPool;
880+
881+ /**
882+ * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator
883+ */
884+ private $batchSizeCalculator;
885+
886+ /**
887+ * @var \Magento\Framework\Indexer\BatchProviderInterface
888+ */
889+ private $batchProvider;
890+
891+ /**
892+ * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
893+ */
894+ private $activeTableSwitcher;
895+
896+ /**
897+ * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
898+ * @param \Magento\Store\Model\StoreManagerInterface $storeManager
899+ * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory
900+ * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
901+ * @param \Magento\Framework\Stdlib\DateTime $dateTime
902+ * @param \Magento\Catalog\Model\Product\Type $catalogProductType
903+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory
904+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource
905+ * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool
906+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator|null $batchSizeCalculator
907+ * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider
908+ * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
909+ *
910+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
911+ */
912+ public function __construct(
913+ \Magento\Framework\App\Config\ScopeConfigInterface $config,
914+ \Magento\Store\Model\StoreManagerInterface $storeManager,
915+ \Magento\Directory\Model\CurrencyFactory $currencyFactory,
916+ \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
917+ \Magento\Framework\Stdlib\DateTime $dateTime,
918+ \Magento\Catalog\Model\Product\Type $catalogProductType,
919+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory,
920+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource,
921+ \Magento\Framework\EntityManager\MetadataPool $metadataPool = null,
922+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator $batchSizeCalculator = null,
923+ \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null,
924+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null
925+ ) {
926+ parent::__construct(
927+ $config,
928+ $storeManager,
929+ $currencyFactory,
930+ $localeDate,
931+ $dateTime,
932+ $catalogProductType,
933+ $indexerPriceFactory,
934+ $defaultIndexerResource
935+ );
936+ $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(
937+ \Magento\Framework\EntityManager\MetadataPool::class
938+ );
939+ $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get(
940+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator::class
941+ );
942+ $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get(
943+ \Magento\Framework\Indexer\BatchProviderInterface::class
944+ );
945+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get(
946+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class
947+ );
948+ }
949+
950 /**
951 * Execute Full reindex
952 *
953 * @param array|int|null $ids
954 * @return void
955 * @throws \Magento\Framework\Exception\LocalizedException
956+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
957 */
958 public function execute($ids = null)
959 {
960 try {
961- $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(true);
962- $this->_emptyTable($this->_defaultIndexerResource->getIdxTable());
963+ $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(false);
964 $this->_prepareWebsiteDateTable();
965- $this->_prepareTierPriceIndex();
966
967+ $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
968+ $replicaTable = $this->activeTableSwitcher->getAdditionalTableName(
969+ $this->_defaultIndexerResource->getMainTable()
970+ );
971+
972+ /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer $indexer */
973 foreach ($this->getTypeIndexers() as $indexer) {
974- $indexer->reindexAll();
975+ $indexer->getTableStrategy()->setUseIdxTable(false);
976+ $connection = $indexer->getConnection();
977+
978+ $batches = $this->batchProvider->getBatches(
979+ $connection,
980+ $entityMetadata->getEntityTable(),
981+ $entityMetadata->getIdentifierField(),
982+ $this->batchSizeCalculator->estimateBatchSize($connection, $indexer->getTypeId())
983+ );
984+
985+ foreach ($batches as $batch) {
986+ // Get entity ids from batch
987+ $select = $connection->select();
988+ $select->distinct(true);
989+ $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField());
990+ $select->where('type_id = ?', $indexer->getTypeId());
991+
992+ $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch);
993+
994+ if (!empty($entityIds)) {
995+ // Temporary table will created if not exists
996+ $idxTableName = $this->_defaultIndexerResource->getIdxTable();
997+ $this->_emptyTable($idxTableName);
998+
999+ if ($indexer->getIsComposite()) {
1000+ $this->_copyRelationIndexData($entityIds);
1001+ }
1002+ $this->_prepareTierPriceIndex($entityIds);
1003+
1004+ // Reindex entities by id
1005+ $indexer->reindexEntity($entityIds);
1006+
1007+ // Sync data from temp table to index table
1008+ $this->_insertFromTable($idxTableName, $replicaTable);
1009+
1010+ // Drop temporary index table
1011+ $connection->dropTable($idxTableName);
1012+ }
1013+ }
1014 }
1015- $this->_syncData();
1016+ $this->activeTableSwitcher->switchTable(
1017+ $this->_defaultIndexerResource->getConnection(),
1018+ [$this->_defaultIndexerResource->getMainTable()]
1019+ );
1020 } catch (\Exception $e) {
1021 throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
1022 }
1023 }
1024+
1025+ /**
1026+ * @inheritdoc
1027+ */
1028+ protected function getIndexTargetTable()
1029+ {
1030+ return $this->activeTableSwitcher->getAdditionalTableName($this->_defaultIndexerResource->getMainTable());
1031+ }
1032 }
1033diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php b/vendor/magento/module-catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php
1034new file mode 100644
1035index 00000000000..6d3451f2d90
1036--- /dev/null
1037+++ b/vendor/magento/module-catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php
1038@@ -0,0 +1,63 @@
1039+<?php
1040+/**
1041+ * Copyright © Magento, Inc. All rights reserved.
1042+ * See COPYING.txt for license details.
1043+ */
1044+namespace Magento\Catalog\Model\ResourceModel\Indexer;
1045+
1046+/**
1047+ * Logic for switching active and replica index tables.
1048+ */
1049+class ActiveTableSwitcher
1050+{
1051+ /** Suffix for replica index table. */
1052+ private $additionalTableSuffix = '_replica';
1053+
1054+ /** Suffix for outdated index table. */
1055+ private $outdatedTableSuffix = '_outdated';
1056+
1057+ /**
1058+ * Switch index tables from replica to active.
1059+ *
1060+ * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
1061+ * @param array $tableNames
1062+ * @return void
1063+ */
1064+ public function switchTable(\Magento\Framework\DB\Adapter\AdapterInterface $connection, array $tableNames)
1065+ {
1066+ $toRename = [];
1067+ foreach ($tableNames as $tableName) {
1068+ $outdatedTableName = $tableName . $this->outdatedTableSuffix;
1069+ $replicaTableName = $tableName . $this->additionalTableSuffix;
1070+
1071+ $renameBatch = [
1072+ [
1073+ 'oldName' => $tableName,
1074+ 'newName' => $outdatedTableName
1075+ ],
1076+ [
1077+ 'oldName' => $replicaTableName,
1078+ 'newName' => $tableName
1079+ ],
1080+ [
1081+ 'oldName' => $outdatedTableName,
1082+ 'newName' => $replicaTableName
1083+ ]
1084+ ];
1085+ $toRename = array_merge($toRename, $renameBatch);
1086+ }
1087+
1088+ if (!empty($toRename)) {
1089+ $connection->renameTablesBatch($toRename);
1090+ }
1091+ }
1092+
1093+ /**
1094+ * @param string $tableName
1095+ * @return string
1096+ */
1097+ public function getAdditionalTableName($tableName)
1098+ {
1099+ return $tableName . $this->additionalTableSuffix;
1100+ }
1101+}
1102diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Layer/Filter/Price.php b/vendor/magento/module-catalog/Model/ResourceModel/Layer/Filter/Price.php
1103index a8b7bf29601..a99d88aa914 100644
1104--- a/vendor/magento/module-catalog/Model/ResourceModel/Layer/Filter/Price.php
1105+++ b/vendor/magento/module-catalog/Model/ResourceModel/Layer/Filter/Price.php
1106@@ -7,6 +7,7 @@ namespace Magento\Catalog\Model\ResourceModel\Layer\Filter;
1107
1108 /**
1109 * Catalog Layer Price Filter resource model
1110+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1111 */
1112 class Price extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
1113 {
1114@@ -43,7 +44,7 @@ class Price extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
1115 * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
1116 * @param \Magento\Customer\Model\Session $session
1117 * @param \Magento\Store\Model\StoreManagerInterface $storeManager
1118- * @param string $connectionName
1119+ * @param null $connectionName
1120 */
1121 public function __construct(
1122 \Magento\Framework\Model\ResourceModel\Db\Context $context,
1123diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php
1124index 7cd1c8c09fc..ca7b8e9badf 100644
1125--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php
1126+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php
1127@@ -11,6 +11,7 @@ use Magento\Catalog\Api\Data\ProductInterface;
1128 * Catalog Product Eav Attributes abstract indexer resource model
1129 *
1130 * @author Magento Core Team <core@magentocommerce.com>
1131+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1132 */
1133 abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer
1134 {
1135@@ -22,13 +23,12 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\
1136 protected $_eventManager = null;
1137
1138 /**
1139- * Construct
1140- *
1141+ * AbstractEav constructor.
1142 * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
1143 * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
1144 * @param \Magento\Eav\Model\Config $eavConfig
1145 * @param \Magento\Framework\Event\ManagerInterface $eventManager
1146- * @param string $connectionName
1147+ * @param null $connectionName
1148 */
1149 public function __construct(
1150 \Magento\Framework\Model\ResourceModel\Db\Context $context,
1151@@ -75,40 +75,11 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\
1152 */
1153 public function reindexEntities($processIds)
1154 {
1155- $connection = $this->getConnection();
1156-
1157 $this->clearTemporaryIndexTable();
1158
1159- if (!is_array($processIds)) {
1160- $processIds = [$processIds];
1161- }
1162-
1163- $parentIds = $this->getRelationsByChild($processIds);
1164- if ($parentIds) {
1165- $processIds = array_unique(array_merge($processIds, $parentIds));
1166- }
1167- $childIds = $this->getRelationsByParent($processIds);
1168- if ($childIds) {
1169- $processIds = array_unique(array_merge($processIds, $childIds));
1170- }
1171-
1172 $this->_prepareIndex($processIds);
1173 $this->_prepareRelationIndex($processIds);
1174 $this->_removeNotVisibleEntityFromIndex();
1175-
1176- $connection->beginTransaction();
1177- try {
1178- // remove old index
1179- $where = $connection->quoteInto('entity_id IN(?)', $processIds);
1180- $connection->delete($this->getMainTable(), $where);
1181-
1182- // insert new index
1183- $this->insertFromTable($this->getIdxTable(), $this->getMainTable());
1184- $connection->commit();
1185- } catch (\Exception $e) {
1186- $connection->rollBack();
1187- throw $e;
1188- }
1189 return $this;
1190 }
1191
1192@@ -218,7 +189,8 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\
1193 ]
1194 );
1195 if ($parentIds !== null) {
1196- $select->where('e.entity_id IN(?)', $parentIds);
1197+ $ids = implode(',', array_map('intval', $parentIds));
1198+ $select->where("e.entity_id IN({$ids})");
1199 }
1200
1201 /**
1202diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/BatchSizeCalculator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/BatchSizeCalculator.php
1203new file mode 100644
1204index 00000000000..479dcf70f4a
1205--- /dev/null
1206+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/BatchSizeCalculator.php
1207@@ -0,0 +1,60 @@
1208+<?php
1209+/**
1210+ * Copyright © Magento, Inc. All rights reserved.
1211+ * See COPYING.txt for license details.
1212+ */
1213+
1214+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav;
1215+
1216+use Magento\Framework\DB\Adapter\AdapterInterface;
1217+use Magento\Framework\Exception\NoSuchEntityException;
1218+
1219+/**
1220+ * Composite batch size calculator for EAV related indexers.
1221+ *
1222+ * Can be configured to provide batch sizes for different indexer types.
1223+ */
1224+class BatchSizeCalculator
1225+{
1226+ /**
1227+ * @var array
1228+ */
1229+ private $batchSizes;
1230+
1231+ /**
1232+ * @var \Magento\Framework\Indexer\BatchSizeManagement[]
1233+ */
1234+ private $batchSizeManagers;
1235+
1236+ /**
1237+ * @param array $batchSizes preferable sizes (number of rows in batch) of batches per index type
1238+ * @param array $batchSizeManagers batch managers per index type
1239+ */
1240+ public function __construct(
1241+ array $batchSizes,
1242+ array $batchSizeManagers
1243+ ) {
1244+ $this->batchSizes = $batchSizes;
1245+ $this->batchSizeManagers = $batchSizeManagers;
1246+ }
1247+
1248+ /**
1249+ * Estimate batch size and ensure that database will be able to handle it properly.
1250+ *
1251+ * @param AdapterInterface $connection
1252+ * @param string $indexerTypeId unique identifier of the indexer
1253+ * @return int estimated batch size
1254+ * @throws NoSuchEntityException thrown if indexer identifier is not recognized
1255+ */
1256+ public function estimateBatchSize(
1257+ AdapterInterface $connection,
1258+ $indexerTypeId
1259+ ) {
1260+ if (!isset($this->batchSizes[$indexerTypeId])) {
1261+ throw NoSuchEntityException::singleField('indexTypeId', $indexerTypeId);
1262+ }
1263+ $this->batchSizeManagers[$indexerTypeId]->ensureBatchSize($connection, $this->batchSizes[$indexerTypeId]);
1264+
1265+ return $this->batchSizes[$indexerTypeId];
1266+ }
1267+}
1268diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/DecimalRowSizeEstimator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/DecimalRowSizeEstimator.php
1269new file mode 100644
1270index 00000000000..0c8c3a9c0b1
1271--- /dev/null
1272+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/DecimalRowSizeEstimator.php
1273@@ -0,0 +1,79 @@
1274+<?php
1275+/**
1276+ * Copyright © Magento, Inc. All rights reserved.
1277+ * See COPYING.txt for license details.
1278+ */
1279+
1280+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav;
1281+
1282+use Magento\Store\Api\StoreManagementInterface;
1283+use Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface;
1284+use Magento\Framework\EntityManager\MetadataPool;
1285+use Magento\Catalog\Api\Data\ProductInterface;
1286+
1287+/**
1288+ * Estimator of the EAV decimal index table row size.
1289+ *
1290+ * Estimates the amount of memory required to store the index data of the product
1291+ * with the highest number of attributes/values.
1292+ *
1293+ * Can be used with batch size manager to ensure that the batch will be handled correctly by the database.
1294+ * @see \Magento\Framework\Indexer\BatchSizeManagement
1295+ */
1296+class DecimalRowSizeEstimator implements IndexTableRowSizeEstimatorInterface
1297+{
1298+ /**
1299+ * @var Decimal
1300+ */
1301+ private $indexerResource;
1302+
1303+ /**
1304+ * @var StoreManagementInterface
1305+ */
1306+ private $storeManagement;
1307+
1308+ /**
1309+ * @var MetadataPool
1310+ */
1311+ private $metadataPool;
1312+
1313+ /**
1314+ * @param StoreManagementInterface $storeManagement
1315+ * @param Decimal $indexerResource
1316+ * @param MetadataPool $metadataPool
1317+ */
1318+ public function __construct(
1319+ StoreManagementInterface $storeManagement,
1320+ Decimal $indexerResource,
1321+ MetadataPool $metadataPool
1322+ ) {
1323+ $this->storeManagement = $storeManagement;
1324+ $this->indexerResource = $indexerResource;
1325+ $this->metadataPool = $metadataPool;
1326+ }
1327+
1328+ /**
1329+ * @inheritdoc
1330+ */
1331+ public function estimateRowSize()
1332+ {
1333+ $connection = $this->indexerResource->getConnection();
1334+ $entityIdField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
1335+
1336+ $valueSelect = $connection->select();
1337+ $valueSelect->from(
1338+ ['value_table' => $this->indexerResource->getTable('catalog_product_entity_decimal')],
1339+ ['count' => new \Zend_Db_Expr('count(value_table.value_id)')]
1340+ );
1341+ $valueSelect->group([$entityIdField, 'store_id']);
1342+
1343+ $maxSelect = $connection->select();
1344+ $maxSelect->from(
1345+ ['max_value' => $valueSelect],
1346+ ['count' => new \Zend_Db_Expr('MAX(count)')]
1347+ );
1348+ $maxRowsPerStore = $connection->fetchOne($maxSelect);
1349+
1350+ return ceil($maxRowsPerStore * $this->storeManagement->getCount() * 500);
1351+ }
1352+}
1353diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
1354index e7cf48c378c..364f7dad61d 100644
1355--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
1356+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
1357@@ -11,10 +11,12 @@ use Magento\Catalog\Api\Data\ProductInterface;
1358 /**
1359 * Catalog Product Eav Select and Multiply Select Attributes Indexer resource model
1360 *
1361- * @author Magento Core Team <core@magentocommerce.com>
1362+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1363 */
1364 class Source extends AbstractEav
1365 {
1366+ const TRANSIT_PREFIX = 'transit_';
1367+
1368 /**
1369 * Catalog resource helper
1370 *
1371@@ -30,7 +32,7 @@ class Source extends AbstractEav
1372 * @param \Magento\Eav\Model\Config $eavConfig
1373 * @param \Magento\Framework\Event\ManagerInterface $eventManager
1374 * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper
1375- * @param string $connectionName
1376+ * @param null|string $connectionName
1377 */
1378 public function __construct(
1379 \Magento\Framework\Model\ResourceModel\Db\Context $context,
1380@@ -40,8 +42,14 @@ class Source extends AbstractEav
1381 \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper,
1382 $connectionName = null
1383 ) {
1384+ parent::__construct(
1385+ $context,
1386+ $tableStrategy,
1387+ $eavConfig,
1388+ $eventManager,
1389+ $connectionName
1390+ );
1391 $this->_resourceHelper = $resourceHelper;
1392- parent::__construct($context, $tableStrategy, $eavConfig, $eventManager, $connectionName);
1393 }
1394
1395 /**
1396@@ -110,30 +118,27 @@ class Source extends AbstractEav
1397 $connection = $this->getConnection();
1398 $idxTable = $this->getIdxTable();
1399 // prepare select attributes
1400- if ($attributeId === null) {
1401- $attrIds = $this->_getIndexableAttributes(false);
1402- } else {
1403- $attrIds = [$attributeId];
1404- }
1405-
1406+ $attrIds = $attributeId === null ? $this->_getIndexableAttributes(false) : [$attributeId];
1407 if (!$attrIds) {
1408 return $this;
1409 }
1410 $productIdField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
1411+ $attrIdsFlat = implode(',', array_map('intval', $attrIds));
1412+ $ifNullSql = $connection->getIfNullSql('pis.value', 'COALESCE(ds.value, dd.value)');
1413
1414- /**@var $subSelect \Magento\Framework\DB\Select*/
1415- $subSelect = $connection->select()->from(
1416+ /**@var $select \Magento\Framework\DB\Select*/
1417+ $select = $connection->select()->distinct(true)->from(
1418 ['s' => $this->getTable('store')],
1419- ['store_id', 'website_id']
1420+ []
1421 )->joinLeft(
1422 ['dd' => $this->getTable('catalog_product_entity_int')],
1423 'dd.store_id = 0',
1424- ['attribute_id']
1425+ []
1426 )->joinLeft(
1427 ['ds' => $this->getTable('catalog_product_entity_int')],
1428 "ds.store_id = s.store_id AND ds.attribute_id = dd.attribute_id AND " .
1429 "ds.{$productIdField} = dd.{$productIdField}",
1430- ['value' => new \Zend_Db_Expr('COALESCE(ds.value, dd.value)')]
1431+ []
1432 )->joinLeft(
1433 ['d2d' => $this->getTable('catalog_product_entity_int')],
1434 sprintf(
1435@@ -149,48 +154,38 @@ class Source extends AbstractEav
1436 )->joinLeft(
1437 ['cpe' => $this->getTable('catalog_product_entity')],
1438 "cpe.{$productIdField} = dd.{$productIdField}",
1439- array_unique([$productIdField, 'entity_id'])
1440+ []
1441+ )->joinLeft(
1442+ ['pis' => $this->getTable('catalog_product_entity_int')],
1443+ "pis.{$productIdField} = cpe.{$productIdField} " .
1444+ "AND pis.attribute_id = dd.attribute_id AND pis.store_id = s.store_id",
1445+ []
1446 )->where(
1447 's.store_id != 0'
1448 )->where(
1449 '(ds.value IS NOT NULL OR dd.value IS NOT NULL)'
1450 )->where(
1451 (new \Zend_Db_Expr('COALESCE(d2s.value, d2d.value)')) . ' = ' . ProductStatus::STATUS_ENABLED
1452- )->distinct(true);
1453-
1454- if ($entityIds !== null) {
1455- $subSelect->where('cpe.entity_id IN(?)', $entityIds);
1456- }
1457-
1458- $ifNullSql = $connection->getIfNullSql('pis.value', 'pid.value');
1459- /**@var $select \Magento\Framework\DB\Select*/
1460- $select = $connection->select()->distinct(true)->from(
1461- ['pid' => new \Zend_Db_Expr(sprintf('(%s)', $subSelect->assemble()))],
1462- []
1463- )->joinLeft(
1464- ['pis' => $this->getTable('catalog_product_entity_int')],
1465- "pis.{$productIdField} = pid.{$productIdField}"
1466- .' AND pis.attribute_id = pid.attribute_id AND pis.store_id = pid.store_id',
1467- []
1468+ )->where(
1469+ "dd.attribute_id IN({$attrIdsFlat})"
1470+ )->where(
1471+ 'NOT(pis.value IS NULL AND pis.value_id IS NOT NULL)'
1472+ )->where(
1473+ $ifNullSql . ' IS NOT NULL'
1474 )->columns(
1475 [
1476- 'pid.entity_id',
1477- 'pid.attribute_id',
1478- 'pid.store_id',
1479- 'value' => $ifNullSql,
1480- 'pid.entity_id',
1481+ 'cpe.entity_id',
1482+ 'dd.attribute_id',
1483+ 's.store_id',
1484+ 'value' => new \Zend_Db_Expr('COALESCE(ds.value, dd.value)'),
1485+ 'cpe.entity_id',
1486 ]
1487- )->where(
1488- 'pid.attribute_id IN(?)',
1489- $attrIds
1490 );
1491
1492- $select->where($ifNullSql . ' IS NOT NULL');
1493-
1494- /**
1495- * Exclude attribute values that contains NULL
1496- */
1497- $select->where('NOT(pis.value IS NULL AND pis.value_id IS NOT NULL)');
1498+ if ($entityIds !== null) {
1499+ $ids = implode(',', array_map('intval', $entityIds));
1500+ $select->where("cpe.entity_id IN({$ids})");
1501+ }
1502
1503 /**
1504 * Add additional external limitation
1505@@ -199,14 +194,13 @@ class Source extends AbstractEav
1506 'prepare_catalog_product_index_select',
1507 [
1508 'select' => $select,
1509- 'entity_field' => new \Zend_Db_Expr('pid.entity_id'),
1510- 'website_field' => new \Zend_Db_Expr('pid.website_id'),
1511- 'store_field' => new \Zend_Db_Expr('pid.store_id'),
1512+ 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'),
1513+ 'website_field' => new \Zend_Db_Expr('s.website_id'),
1514+ 'store_field' => new \Zend_Db_Expr('s.store_id'),
1515 ]
1516 );
1517 $query = $select->insertFromSelect($idxTable);
1518 $connection->query($query);
1519-
1520 return $this;
1521 }
1522
1523@@ -325,11 +319,8 @@ class Source extends AbstractEav
1524 }
1525
1526 /**
1527- * Prepares data from select to save.
1528- *
1529 * @param \Magento\Framework\DB\Select $select
1530 * @param array $options
1531- *
1532 * @return void
1533 */
1534 private function saveDataFromSelect(\Magento\Framework\DB\Select $select, array $options)
1535@@ -365,4 +356,48 @@ class Source extends AbstractEav
1536 {
1537 return $this->tableStrategy->getTableName('catalog_product_index_eav');
1538 }
1539+
1540+ /**
1541+ * Prepare data index for product relations
1542+ *
1543+ * @param array $parentIds the parent entity ids limitation
1544+ * @return $this
1545+ */
1546+ protected function _prepareRelationIndex($parentIds = null)
1547+ {
1548+ $connection = $this->getConnection();
1549+ $idxTable = $this->getIdxTable();
1550+
1551+ if (!$this->tableStrategy->getUseIdxTable()) {
1552+ $additionalIdxTable = $connection->getTableName(self::TRANSIT_PREFIX . $this->getIdxTable());
1553+ $connection->createTemporaryTableLike($additionalIdxTable, $idxTable);
1554+
1555+ $query = $connection->insertFromSelect(
1556+ $this->_prepareRelationIndexSelect($parentIds),
1557+ $additionalIdxTable,
1558+ []
1559+ );
1560+ $connection->query($query);
1561+
1562+ $select = $connection->select()->from($additionalIdxTable);
1563+ $query = $connection->insertFromSelect(
1564+ $select,
1565+ $idxTable,
1566+ [],
1567+ \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
1568+ );
1569+ $connection->query($query);
1570+
1571+ $connection->dropTemporaryTable($additionalIdxTable);
1572+ } else {
1573+ $query = $connection->insertFromSelect(
1574+ $this->_prepareRelationIndexSelect($parentIds),
1575+ $idxTable,
1576+ [],
1577+ \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
1578+ );
1579+ $connection->query($query);
1580+ }
1581+ return $this;
1582+ }
1583 }
1584diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/SourceRowSizeEstimator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/SourceRowSizeEstimator.php
1585new file mode 100644
1586index 00000000000..d212337ae7c
1587--- /dev/null
1588+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/SourceRowSizeEstimator.php
1589@@ -0,0 +1,107 @@
1590+<?php
1591+/**
1592+ * Copyright © Magento, Inc. All rights reserved.
1593+ * See COPYING.txt for license details.
1594+ */
1595+
1596+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav;
1597+
1598+use Magento\Store\Api\StoreManagementInterface;
1599+use Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface;
1600+use Magento\Framework\EntityManager\MetadataPool;
1601+use Magento\Catalog\Api\Data\ProductInterface;
1602+use Magento\Framework\DB\Adapter\AdapterInterface;
1603+
1604+/**
1605+ * Estimator of the EAV index table row size.
1606+ *
1607+ * Estimates the amount of memory required to store the index data of the product
1608+ * with the highest number of attributes/values.
1609+ *
1610+ * Can be used with batch size manager to ensure that the batch will be handled correctly by the database.
1611+ * @see \Magento\Framework\Indexer\BatchSizeManagement
1612+ */
1613+class SourceRowSizeEstimator implements IndexTableRowSizeEstimatorInterface
1614+{
1615+ /**
1616+ * @var StoreManagementInterface
1617+ */
1618+ private $storeManagement;
1619+
1620+ /**
1621+ * @var Source
1622+ */
1623+ private $indexerResource;
1624+
1625+ /**
1626+ * @var MetadataPool
1627+ */
1628+ private $metadataPool;
1629+
1630+ /**
1631+ * @param StoreManagementInterface $storeManagement
1632+ * @param Source $indexerResource
1633+ * @param MetadataPool $metadataPool
1634+ */
1635+ public function __construct(
1636+ StoreManagementInterface $storeManagement,
1637+ Source $indexerResource,
1638+ MetadataPool $metadataPool
1639+ ) {
1640+ $this->storeManagement = $storeManagement;
1641+ $this->indexerResource = $indexerResource;
1642+ $this->metadataPool = $metadataPool;
1643+ }
1644+
1645+ /**
1646+ * @inheritdoc
1647+ */
1648+ public function estimateRowSize()
1649+ {
1650+ $connection = $this->indexerResource->getConnection();
1651+ $entityIdField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
1652+
1653+ $maxRowsPerStore = max(
1654+ $this->getMaxRowsPerStore(
1655+ $connection,
1656+ $this->indexerResource->getTable('catalog_product_entity_int'),
1657+ $entityIdField
1658+ ),
1659+ $this->getMaxRowsPerStore(
1660+ $connection,
1661+ $this->indexerResource->getTable('catalog_product_entity_varchar'),
1662+ $entityIdField
1663+ )
1664+ );
1665+
1666+ return ceil($maxRowsPerStore * $this->storeManagement->getCount() * 500);
1667+ }
1668+
1669+ /**
1670+ * Calculate maximum rows per store and product stored in the target table.
1671+ *
1672+ * @param AdapterInterface $connection
1673+ * @param string $valueTable name of the target table
1674+ * @param string $entityIdField entity ID field name
1675+ * @return string maximum rows per store and product stored in the table
1676+ */
1677+ private function getMaxRowsPerStore(
1678+ AdapterInterface $connection,
1679+ $valueTable,
1680+ $entityIdField
1681+ ) {
1682+ $valueSelect = $connection->select();
1683+ $valueSelect->from(
1684+ ['value_table' => $valueTable],
1685+ ['count' => new \Zend_Db_Expr('count(value_table.value_id)')]
1686+ );
1687+ $valueSelect->group([$entityIdField, 'store_id']);
1688+
1689+ $maxSelect = $connection->select();
1690+ $maxSelect->from(
1691+ ['max_value' => $valueSelect],
1692+ ['count' => new \Zend_Db_Expr('MAX(count)')]
1693+ );
1694+ return $connection->fetchOne($maxSelect);
1695+ }
1696+}
1697diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
1698index ffaf8a5d100..d81b38e9ac7 100644
1699--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
1700+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
1701@@ -39,11 +39,12 @@ class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuild
1702 private $baseSelectProcessor;
1703
1704 /**
1705+ * LinkedProductSelectBuilderByIndexPrice constructor.
1706 * @param \Magento\Store\Model\StoreManagerInterface $storeManager
1707 * @param \Magento\Framework\App\ResourceConnection $resourceConnection
1708 * @param \Magento\Customer\Model\Session $customerSession
1709 * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
1710- * @param BaseSelectProcessorInterface $baseSelectProcessor
1711+ * @param BaseSelectProcessorInterface|null $baseSelectProcessor
1712 */
1713 public function __construct(
1714 \Magento\Store\Model\StoreManagerInterface $storeManager,
1715diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php
1716new file mode 100644
1717index 00000000000..b31147af234
1718--- /dev/null
1719+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/BatchSizeCalculator.php
1720@@ -0,0 +1,59 @@
1721+<?php
1722+/**
1723+ * Copyright © Magento, Inc. All rights reserved.
1724+ * See COPYING.txt for license details.
1725+ */
1726+
1727+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price;
1728+
1729+/**
1730+ * Ensure that size of index MEMORY table is enough for configured rows count in batch.
1731+ */
1732+class BatchSizeCalculator
1733+{
1734+ /**
1735+ * @var array
1736+ */
1737+ private $batchRowsCount;
1738+
1739+ /**
1740+ * @var \Magento\Framework\Indexer\BatchSizeManagementInterface[]
1741+ */
1742+ private $estimators;
1743+
1744+ /**
1745+ * BatchSizeCalculator constructor.
1746+ * @param array $batchRowsCount
1747+ * @param array $estimators
1748+ */
1749+ public function __construct(array $batchRowsCount, array $estimators)
1750+ {
1751+ $this->batchRowsCount = $batchRowsCount;
1752+ $this->estimators = $estimators;
1753+ }
1754+
1755+ /**
1756+ * Retrieve batch size for the given indexer.
1757+ *
1758+ * Ensure that the database will be able to handle provided batch size correctly.
1759+ *
1760+ * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
1761+ * @param string $indexerTypeId
1762+ * @return int
1763+ */
1764+ public function estimateBatchSize(\Magento\Framework\DB\Adapter\AdapterInterface $connection, $indexerTypeId)
1765+ {
1766+ $batchRowsCount = isset($this->batchRowsCount[$indexerTypeId])
1767+ ? $this->batchRowsCount[$indexerTypeId]
1768+ : $this->batchRowsCount['default'];
1769+
1770+ /** @var \Magento\Framework\Indexer\BatchSizeManagementInterface $calculator */
1771+ $calculator = isset($this->estimators[$indexerTypeId])
1772+ ? $this->estimators[$indexerTypeId]
1773+ : $this->estimators['default'];
1774+
1775+ $calculator->ensureBatchSize($connection, $batchRowsCount);
1776+
1777+ return $batchRowsCount;
1778+ }
1779+}
1780diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php
1781new file mode 100644
1782index 00000000000..a3425bfc9d7
1783--- /dev/null
1784+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/CompositeProductRowSizeEstimator.php
1785@@ -0,0 +1,88 @@
1786+<?php
1787+/**
1788+ * Copyright © Magento, Inc. All rights reserved.
1789+ * See COPYING.txt for license details.
1790+ */
1791+
1792+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price;
1793+
1794+use Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface;
1795+use Magento\Store\Api\WebsiteManagementInterface;
1796+use Magento\Customer\Model\ResourceModel\Group\CollectionFactory;
1797+
1798+/**
1799+ * Estimate index memory size for largest composite product in catalog.
1800+ */
1801+class CompositeProductRowSizeEstimator implements IndexTableRowSizeEstimatorInterface
1802+{
1803+ /**
1804+ * Calculated memory size for one record in catalog_product_index_price table
1805+ */
1806+ const MEMORY_SIZE_FOR_ONE_ROW = 200;
1807+
1808+ /**
1809+ * @var DefaultPrice
1810+ */
1811+ private $indexerResource;
1812+
1813+ /**
1814+ * @var WebsiteManagementInterface
1815+ */
1816+ private $websiteManagement;
1817+
1818+ /**
1819+ * @var CollectionFactory
1820+ */
1821+ private $collectionFactory;
1822+
1823+ /**
1824+ * @param DefaultPrice $indexerResource
1825+ * @param WebsiteManagementInterface $websiteManagement
1826+ * @param CollectionFactory $collectionFactory
1827+ */
1828+ public function __construct(
1829+ DefaultPrice $indexerResource,
1830+ WebsiteManagementInterface $websiteManagement,
1831+ CollectionFactory $collectionFactory
1832+ ) {
1833+ $this->indexerResource = $indexerResource;
1834+ $this->websiteManagement = $websiteManagement;
1835+ $this->collectionFactory = $collectionFactory;
1836+ }
1837+
1838+ /**
1839+ * Calculate memory size for largest composite product in database.
1840+ *
1841+ * {@inheritdoc}
1842+ */
1843+ public function estimateRowSize()
1844+ {
1845+ $websitesCount = $this->websiteManagement->getCount();
1846+ $customerGroupCount = $this->collectionFactory->create()->getSize();
1847+
1848+ $connection = $this->indexerResource->getConnection();
1849+ $relationSelect = $connection->select();
1850+ $relationSelect->from(
1851+ ['relation' => $this->indexerResource->getTable('catalog_product_relation')],
1852+ ['count' => new \Zend_Db_Expr('count(relation.child_id)')]
1853+ );
1854+ $relationSelect->group('parent_id');
1855+
1856+ $maxSelect = $connection->select();
1857+ $maxSelect->from(
1858+ ['max_value' => $relationSelect],
1859+ ['count' => new \Zend_Db_Expr('MAX(count)')]
1860+ );
1861+ $maxRelatedProductCount = $connection->fetchOne($maxSelect);
1862+
1863+ /**
1864+ * Calculate memory size for largest composite product in database.
1865+ *
1866+ * $maxRelatedProductCount - maximum number of related products
1867+ * $websitesCount - active websites
1868+ * $customerGroupCount - active customer groups
1869+ * MEMORY_SIZE_FOR_ONE_ROW - calculated memory size for one record in catalog_product_index_price table
1870+ */
1871+ return ceil($maxRelatedProductCount * $websitesCount * $customerGroupCount * self::MEMORY_SIZE_FOR_ONE_ROW);
1872+ }
1873+}
1874diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
1875index 8a97d43a18d..5d1353ccd57 100644
1876--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
1877+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
1878@@ -12,6 +12,7 @@ use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer;
1879 * For correctly work need define product type id
1880 *
1881 * @author Magento Core Team <core@magentocommerce.com>
1882+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1883 */
1884 class DefaultPrice extends AbstractIndexer implements PriceInterface
1885 {
1886@@ -44,14 +45,19 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface
1887 protected $_eventManager = null;
1888
1889 /**
1890- * Class constructor
1891+ * @var bool|null
1892+ */
1893+ private $hasEntity = null;
1894+
1895+ /**
1896+ * DefaultPrice constructor.
1897 *
1898 * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
1899 * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
1900 * @param \Magento\Eav\Model\Config $eavConfig
1901 * @param \Magento\Framework\Event\ManagerInterface $eventManager
1902 * @param \Magento\Framework\Module\Manager $moduleManager
1903- * @param string $connectionName
1904+ * @param string|null $connectionName
1905 */
1906 public function __construct(
1907 \Magento\Framework\Model\ResourceModel\Db\Context $context,
1908@@ -677,16 +683,19 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface
1909 */
1910 protected function hasEntity()
1911 {
1912- $reader = $this->getConnection();
1913-
1914- $select = $reader->select()->from(
1915- [$this->getTable('catalog_product_entity')],
1916- ['count(entity_id)']
1917- )->where(
1918- 'type_id=?',
1919- $this->getTypeId()
1920- );
1921+ if ($this->hasEntity === null) {
1922+ $reader = $this->getConnection();
1923+
1924+ $select = $reader->select()->from(
1925+ [$this->getTable('catalog_product_entity')],
1926+ ['count(entity_id)']
1927+ )->where(
1928+ 'type_id=?',
1929+ $this->getTypeId()
1930+ );
1931+ $this->hasEntity = (int)$reader->fetchOne($select) > 0;
1932+ }
1933
1934- return (int)$reader->fetchOne($select) > 0;
1935+ return $this->hasEntity;
1936 }
1937 }
1938diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableRowSizeEstimator.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableRowSizeEstimator.php
1939new file mode 100644
1940index 00000000000..89df6677f24
1941--- /dev/null
1942+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableRowSizeEstimator.php
1943@@ -0,0 +1,63 @@
1944+<?php
1945+/**
1946+ * Copyright © Magento, Inc. All rights reserved.
1947+ * See COPYING.txt for license details.
1948+ */
1949+
1950+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price;
1951+
1952+/**
1953+ * Estimate index memory size for simple product.
1954+ * Size depends on websites and customer groups count.
1955+ */
1956+class IndexTableRowSizeEstimator implements \Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface
1957+{
1958+ /**
1959+ * Calculated memory size for one record in catalog_product_index_price table
1960+ */
1961+ const MEMORY_SIZE_FOR_ONE_ROW = 120;
1962+
1963+ /**
1964+ * @var \Magento\Store\Api\WebsiteManagementInterface
1965+ */
1966+ private $websiteManagement;
1967+
1968+ /**
1969+ * @var \Magento\Customer\Model\ResourceModel\Group\CollectionFactory
1970+ */
1971+ private $collectionFactory;
1972+
1973+ /**
1974+ * CompositeProductBatchSizeCalculator constructor.
1975+ * @param \Magento\Store\Api\WebsiteManagementInterface $websiteManagement
1976+ * @param \Magento\Customer\Model\ResourceModel\Group\CollectionFactory $collectionFactory
1977+ */
1978+ public function __construct(
1979+ \Magento\Store\Api\WebsiteManagementInterface $websiteManagement,
1980+ \Magento\Customer\Model\ResourceModel\Group\CollectionFactory $collectionFactory
1981+ ) {
1982+ $this->websiteManagement = $websiteManagement;
1983+ $this->collectionFactory = $collectionFactory;
1984+ }
1985+
1986+ /**
1987+ * @inheritdoc
1988+ */
1989+ public function estimateRowSize()
1990+ {
1991+ $websitesCount = $this->websiteManagement->getCount();
1992+
1993+ /** @var \Magento\Customer\Model\ResourceModel\Group\Collection $collection */
1994+ $collection = $this->collectionFactory->create();
1995+ $customerGroupCount = $collection->getSize();
1996+
1997+ /**
1998+ * Calculate memory size for product in database.
1999+ *
2000+ * $websitesCount - active websites
2001+ * $customerGroupCount - active customer groups
2002+ * MEMORY_SIZE_FOR_ONE_ROW - calculated memory size for one record in catalog_product_index_price table
2003+ */
2004+ return ceil($websitesCount * $customerGroupCount * self::MEMORY_SIZE_FOR_ONE_ROW);
2005+ }
2006+}
2007diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/TemporaryTableStrategy.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/TemporaryTableStrategy.php
2008new file mode 100644
2009index 00000000000..54673cb01bb
2010--- /dev/null
2011+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/TemporaryTableStrategy.php
2012@@ -0,0 +1,87 @@
2013+<?php
2014+/**
2015+ * Copyright © Magento, Inc. All rights reserved.
2016+ * See COPYING.txt for license details.
2017+ */
2018+
2019+namespace Magento\Catalog\Model\ResourceModel\Product\Indexer;
2020+
2021+/**
2022+ * Provided logic will create temporary table based on memory table and will return new index table name.
2023+ */
2024+class TemporaryTableStrategy implements \Magento\Framework\Indexer\Table\StrategyInterface
2025+{
2026+ /**
2027+ * Suffix for new temporary table
2028+ */
2029+ const TEMP_SUFFIX = '_temp';
2030+
2031+ /**
2032+ * @var \Magento\Framework\Indexer\Table\Strategy
2033+ */
2034+ private $strategy;
2035+
2036+ /**
2037+ * Application resource
2038+ *
2039+ * @var \Magento\Framework\App\ResourceConnection
2040+ */
2041+ private $resource;
2042+
2043+ /**
2044+ * TemporaryTableStrategy constructor.
2045+ * @param \Magento\Framework\Indexer\Table\Strategy $strategy
2046+ * @param \Magento\Framework\App\ResourceConnection $resource
2047+ */
2048+ public function __construct(
2049+ \Magento\Framework\Indexer\Table\StrategyInterface $strategy,
2050+ \Magento\Framework\App\ResourceConnection $resource
2051+ ) {
2052+ $this->strategy = $strategy;
2053+ $this->resource = $resource;
2054+ }
2055+
2056+ /**
2057+ * @inheritdoc
2058+ */
2059+ public function getUseIdxTable()
2060+ {
2061+ return $this->strategy->getUseIdxTable();
2062+ }
2063+
2064+ /**
2065+ * @inheritdoc
2066+ */
2067+ public function setUseIdxTable($value = false)
2068+ {
2069+ return $this->strategy->setUseIdxTable($value);
2070+ }
2071+
2072+ /**
2073+ * @inheritdoc
2074+ */
2075+ public function getTableName($tablePrefix)
2076+ {
2077+ return $this->resource->getTableName($this->prepareTableName($tablePrefix));
2078+ }
2079+
2080+ /**
2081+ * Create temporary index table based on memory table
2082+ *
2083+ * {@inheritdoc}
2084+ */
2085+ public function prepareTableName($tablePrefix)
2086+ {
2087+ if ($this->getUseIdxTable()) {
2088+ return $tablePrefix . self::IDX_SUFFIX;
2089+ }
2090+
2091+ // Create temporary table
2092+ $this->resource->getConnection('indexer')->createTemporaryTableLike(
2093+ $this->resource->getTableName($tablePrefix . self::TEMP_SUFFIX),
2094+ $this->resource->getTableName($tablePrefix . self::TMP_SUFFIX),
2095+ true
2096+ );
2097+ return $tablePrefix . self::TEMP_SUFFIX;
2098+ }
2099+}
2100diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Url.php b/vendor/magento/module-catalog/Model/ResourceModel/Url.php
2101index cb4c3143fc3..c4bab809c2f 100644
2102--- a/vendor/magento/module-catalog/Model/ResourceModel/Url.php
2103+++ b/vendor/magento/module-catalog/Model/ResourceModel/Url.php
2104@@ -13,6 +13,7 @@ namespace Magento\Catalog\Model\ResourceModel;
2105 use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
2106 use Magento\Catalog\Api\Data\CategoryInterface;
2107 use Magento\Framework\EntityManager\MetadataPool;
2108+use Magento\Framework\App\ObjectManager;
2109
2110 /**
2111 * Class Url
2112diff --git a/vendor/magento/module-catalog/Setup/UpgradeSchema.php b/vendor/magento/module-catalog/Setup/UpgradeSchema.php
2113index 213cbe8b0cf..b46c8cfd91e 100644
2114--- a/vendor/magento/module-catalog/Setup/UpgradeSchema.php
2115+++ b/vendor/magento/module-catalog/Setup/UpgradeSchema.php
2116@@ -37,6 +37,56 @@ class UpgradeSchema implements UpgradeSchemaInterface
2117 $this->addSourceEntityIdToProductEavIndex($setup);
2118 }
2119
2120+ if (version_compare($context->getVersion(), '2.1.4.1.2', '<')) {
2121+ //remove fk from price index table
2122+ $setup->getConnection()->dropForeignKey(
2123+ $setup->getTable('catalog_product_index_price'),
2124+ $setup->getFkName(
2125+ 'catalog_product_index_price',
2126+ 'entity_id',
2127+ 'catalog_product_entity',
2128+ 'entity_id'
2129+ )
2130+ );
2131+ $setup->getConnection()->dropForeignKey(
2132+ $setup->getTable('catalog_product_index_price'),
2133+ $setup->getFkName(
2134+ 'catalog_product_index_price',
2135+ 'website_id',
2136+ 'store_website',
2137+ 'website_id'
2138+ )
2139+ );
2140+ $setup->getConnection()->dropForeignKey(
2141+ $setup->getTable('catalog_product_index_price'),
2142+ $setup->getFkName(
2143+ 'catalog_product_index_price',
2144+ 'customer_group_id',
2145+ 'customer_group',
2146+ 'customer_group_id'
2147+ )
2148+ );
2149+
2150+ $this->addReplicaTable($setup, 'catalog_product_index_eav', 'catalog_product_index_eav_replica');
2151+ $this->addReplicaTable(
2152+ $setup,
2153+ 'catalog_product_index_eav_decimal',
2154+ 'catalog_product_index_eav_decimal_replica'
2155+ );
2156+ // By adding 'catalog_product_index_price_replica' we provide separation of tables
2157+ // used for indexation write and read operations and affected models.
2158+ $this->addReplicaTable(
2159+ $setup,
2160+ 'catalog_product_index_price',
2161+ 'catalog_product_index_price_replica'
2162+ );
2163+ // the same for 'catalog_category_product_index'
2164+ $this->addReplicaTable(
2165+ $setup,
2166+ 'catalog_category_product_index',
2167+ 'catalog_category_product_index_replica'
2168+ );
2169+ }
2170 $setup->endSetup();
2171 }
2172
2173@@ -46,7 +96,6 @@ class UpgradeSchema implements UpgradeSchemaInterface
2174 * It is useful to identify original entity in a composite products.
2175 *
2176 * @param SchemaSetupInterface $setup
2177- *
2178 * @return void
2179 */
2180 private function addSourceEntityIdToProductEavIndex(SchemaSetupInterface $setup)
2181@@ -60,7 +109,6 @@ class UpgradeSchema implements UpgradeSchemaInterface
2182 'catalog_product_index_eav_decimal_tmp',
2183 ];
2184 $connection = $setup->getConnection();
2185-
2186 foreach ($tables as $tableName) {
2187 $tableName = $setup->getTable($tableName);
2188 $connection->addColumn(
2189@@ -328,4 +376,22 @@ class UpgradeSchema implements UpgradeSchemaInterface
2190 $connection->dropColumn($setup->getTable($filedInfo['table']), $filedInfo['column']);
2191 }
2192 }
2193+
2194+ /**
2195+ * Add the replica table for existing one.
2196+ *
2197+ * @param SchemaSetupInterface $setup
2198+ * @param string $existingTable
2199+ * @param string $replicaTable
2200+ * @return void
2201+ */
2202+ private function addReplicaTable(SchemaSetupInterface $setup, $existingTable, $replicaTable)
2203+ {
2204+ $sql = sprintf(
2205+ 'CREATE TABLE IF NOT EXISTS %s LIKE %s',
2206+ $setup->getConnection()->quoteIdentifier($setup->getTable($replicaTable)),
2207+ $setup->getConnection()->quoteIdentifier($setup->getTable($existingTable))
2208+ );
2209+ $setup->getConnection()->query($sql);
2210+ }
2211 }
2212diff --git a/vendor/magento/module-catalog/etc/di.xml b/vendor/magento/module-catalog/etc/di.xml
2213index 793c6351ffe..469e4d7cc17 100644
2214--- a/vendor/magento/module-catalog/etc/di.xml
2215+++ b/vendor/magento/module-catalog/etc/di.xml
2216@@ -46,6 +46,7 @@
2217 <preference for="Magento\Catalog\Api\ProductManagementInterface" type="Magento\Catalog\Model\ProductManagement" />
2218 <preference for="Magento\Catalog\Api\AttributeSetFinderInterface" type="Magento\Catalog\Model\Product\Attribute\AttributeSetFinder" />
2219 <preference for="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface" type="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver"/>
2220+ <preference for="Magento\Framework\Indexer\BatchProviderInterface" type="Magento\Framework\Indexer\BatchProvider" />
2221 <type name="Magento\Customer\Model\ResourceModel\Visitor">
2222 <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
2223 </type>
2224@@ -806,4 +807,78 @@
2225 <plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/>
2226 </type>
2227 <preference for="Magento\Catalog\Pricing\Price\MinimalPriceCalculatorInterface" type="Magento\Catalog\Pricing\Price\MinimalTierPriceCalculator" />
2228+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator">
2229+ <arguments>
2230+ <argument name="batchRowsCount" xsi:type="array">
2231+ <item name="default" xsi:type="number">5000</item></argument>
2232+ <argument name="estimators" xsi:type="array">
2233+ <item name="default" xsi:type="object">Magento\Catalog\Model\Indexer\Price\BatchSizeManagement</item>
2234+ </argument>
2235+ </arguments>
2236+ </type>
2237+ <virtualType name="Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement" type="Magento\Framework\Indexer\BatchSizeManagement">
2238+ <arguments>
2239+ <argument name="rowSizeEstimator" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\CompositeProductRowSizeEstimator</argument>
2240+ </arguments>
2241+ </virtualType>
2242+ <virtualType name="Magento\Catalog\Model\Indexer\Price\BatchSizeManagement" type="Magento\Framework\Indexer\BatchSizeManagement">
2243+ <arguments>
2244+ <argument name="rowSizeEstimator" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableRowSizeEstimator</argument>
2245+ </arguments>
2246+ </virtualType>
2247+ <type name="Magento\Catalog\Model\Indexer\Category\Product\Action\Full">
2248+ <arguments>
2249+ <argument name="batchRowsCount" xsi:type="number">100000</argument>
2250+ <argument name="batchSizeManagement" xsi:type="object">Magento\Catalog\Model\Indexer\CategoryProductBatchSize</argument>
2251+ </arguments>
2252+ </type>
2253+ <virtualType name="Magento\Catalog\Model\Indexer\CategoryProductBatchSize" type="Magento\Framework\Indexer\BatchSizeManagement">
2254+ <arguments>
2255+ <argument name="rowSizeEstimator" xsi:type="object">Magento\Catalog\Model\Indexer\Category\Product\RowSizeEstimator</argument>
2256+ </arguments>
2257+ </virtualType>
2258+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy" shared="false">
2259+ <arguments>
2260+ <argument name="strategy" xsi:type="object">Magento\Framework\Indexer\Table\Strategy</argument>
2261+ </arguments>
2262+ </type>
2263+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice">
2264+ <arguments>
2265+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
2266+ <argument name="connectionName" xsi:type="string">indexer</argument>
2267+ </arguments>
2268+ </type>
2269+ <type name="Magento\Catalog\Model\Indexer\Product\Eav\Action\Full">
2270+ <arguments>
2271+ <argument name="batchSizeCalculator" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator</argument>
2272+ </arguments>
2273+ </type>
2274+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source">
2275+ <arguments>
2276+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
2277+ <argument name="connectionName" xsi:type="string">indexer</argument>
2278+ </arguments>
2279+ </type>
2280+ <virtualType name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalBatchSizeManagement" type="Magento\Framework\Indexer\BatchSizeManagement">
2281+ <arguments>
2282+ <argument name="rowSizeEstimator" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalRowSizeEstimator</argument>
2283+ </arguments>
2284+ </virtualType>
2285+ <virtualType name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceBatchSizeManagement" type="Magento\Framework\Indexer\BatchSizeManagement">
2286+ <arguments>
2287+ <argument name="rowSizeEstimator" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceRowSizeEstimator</argument>
2288+ </arguments>
2289+ </virtualType>
2290+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator">
2291+ <arguments>
2292+ <argument name="batchSizes" xsi:type="array">
2293+ <item name="decimal" xsi:type="number">1000</item>
2294+ <item name="source" xsi:type="number">1000</item>
2295+ </argument>
2296+ <argument name="batchSizeManagers" xsi:type="array">
2297+ <item name="decimal" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalBatchSizeManagement</item>
2298+ <item name="source" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceBatchSizeManagement</item>
2299+ </argument>
2300+ </arguments>
2301+ </type>
2302 </config>
2303diff --git a/vendor/magento/module-catalog/etc/module.xml b/vendor/magento/module-catalog/etc/module.xml
2304index 57117669454..d3dd9716abe 100644
2305--- a/vendor/magento/module-catalog/etc/module.xml
2306+++ b/vendor/magento/module-catalog/etc/module.xml
2307@@ -6,7 +6,7 @@
2308 */
2309 -->
2310 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
2311- <module name="Magento_Catalog" setup_version="2.1.4">
2312+ <module name="Magento_Catalog" setup_version="2.1.4.1.2">
2313 <sequence>
2314 <module name="Magento_Eav"/>
2315 <module name="Magento_Cms"/>
2316diff --git a/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/AbstractAction.php b/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/AbstractAction.php
2317index cd4a3b440ab..6bbcc9dde1a 100644
2318--- a/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/AbstractAction.php
2319+++ b/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/AbstractAction.php
2320@@ -186,10 +186,10 @@ abstract class AbstractAction
2321
2322 $this->_deleteOldRelations($tableName);
2323
2324- $columns = array_keys($this->_connection->describeTable($idxTableName));
2325- $select = $this->_connection->select()->from($idxTableName, $columns);
2326+ $columns = array_keys($this->_getConnection()->describeTable($idxTableName));
2327+ $select = $this->_getConnection()->select()->from($idxTableName, $columns);
2328 $query = $select->insertFromSelect($tableName, $columns);
2329- $this->_connection->query($query);
2330+ $this->_getConnection()->query($query);
2331 return $this;
2332 }
2333
2334@@ -202,7 +202,7 @@ abstract class AbstractAction
2335 */
2336 protected function _deleteOldRelations($tableName)
2337 {
2338- $select = $this->_connection->select()
2339+ $select = $this->_getConnection()->select()
2340 ->from(['s' => $tableName])
2341 ->joinLeft(
2342 ['w' => $this->_getTable('catalog_product_website')],
2343@@ -212,7 +212,7 @@ abstract class AbstractAction
2344 ->where('w.product_id IS NULL');
2345
2346 $sql = $select->deleteFromSelect('s');
2347- $this->_connection->query($sql);
2348+ $this->_getConnection()->query($sql);
2349 }
2350
2351 /**
2352diff --git a/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/Action/Full.php b/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/Action/Full.php
2353index 2f545417a07..baa770c25bc 100644
2354--- a/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/Action/Full.php
2355+++ b/vendor/magento/module-catalog-inventory/Model/Indexer/Stock/Action/Full.php
2356@@ -8,18 +8,106 @@
2357
2358 namespace Magento\CatalogInventory\Model\Indexer\Stock\Action;
2359
2360+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
2361+use Magento\Framework\App\ResourceConnection;
2362+use Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory;
2363+use Magento\Catalog\Model\Product\Type as ProductType;
2364+use Magento\Framework\Indexer\CacheContext;
2365+use Magento\Framework\Event\ManagerInterface as EventManager;
2366+use Magento\Framework\EntityManager\MetadataPool;
2367+use Magento\Framework\Indexer\BatchSizeManagementInterface;
2368+use Magento\Framework\Indexer\BatchProviderInterface;
2369+use Magento\Framework\App\ObjectManager;
2370+use Magento\Framework\Exception\LocalizedException;
2371+use Magento\CatalogInventory\Model\Indexer\Stock\AbstractAction;
2372+
2373 /**
2374 * Class Full reindex action
2375 *
2376 * @package Magento\CatalogInventory\Model\Indexer\Stock\Action
2377+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2378 */
2379-class Full extends \Magento\CatalogInventory\Model\Indexer\Stock\AbstractAction
2380+class Full extends AbstractAction
2381 {
2382+ /**
2383+ * Action type representation
2384+ */
2385+ const ACTION_TYPE = 'full';
2386+
2387+ /**
2388+ * @var MetadataPool
2389+ */
2390+ private $metadataPool;
2391+
2392+ /**
2393+ * @var BatchSizeManagementInterface
2394+ */
2395+ private $batchSizeManagement;
2396+
2397+ /**
2398+ * @var BatchProviderInterface
2399+ */
2400+ private $batchProvider;
2401+
2402+ /**
2403+ * @var array
2404+ */
2405+ private $batchRowsCount;
2406+
2407+ /**
2408+ * @var ActiveTableSwitcher
2409+ */
2410+ private $activeTableSwitcher;
2411+
2412+ /**
2413+ * @param ResourceConnection $resource
2414+ * @param StockFactory $indexerFactory
2415+ * @param ProductType $catalogProductType
2416+ * @param CacheContext $cacheContext
2417+ * @param EventManager $eventManager
2418+ * @param MetadataPool|null $metadataPool
2419+ * @param BatchSizeManagementInterface|null $batchSizeManagement
2420+ * @param BatchProviderInterface|null $batchProvider
2421+ * @param array $batchRowsCount
2422+ * @param ActiveTableSwitcher|null $activeTableSwitcher
2423+ *
2424+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
2425+ */
2426+ public function __construct(
2427+ ResourceConnection $resource,
2428+ StockFactory $indexerFactory,
2429+ ProductType $catalogProductType,
2430+ CacheContext $cacheContext,
2431+ EventManager $eventManager,
2432+ MetadataPool $metadataPool = null,
2433+ BatchSizeManagementInterface $batchSizeManagement = null,
2434+ BatchProviderInterface $batchProvider = null,
2435+ array $batchRowsCount = [],
2436+ ActiveTableSwitcher $activeTableSwitcher = null
2437+ ) {
2438+ parent::__construct(
2439+ $resource,
2440+ $indexerFactory,
2441+ $catalogProductType,
2442+ $cacheContext,
2443+ $eventManager
2444+ );
2445+
2446+ $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class);
2447+ $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get(BatchProviderInterface::class);
2448+ $this->batchSizeManagement = $batchSizeManagement ?: ObjectManager::getInstance()->get(
2449+ \Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement::class
2450+ );
2451+ $this->batchRowsCount = $batchRowsCount;
2452+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()
2453+ ->get(ActiveTableSwitcher::class);
2454+ }
2455+
2456 /**
2457 * Execute Full reindex
2458 *
2459 * @param null|array $ids
2460- * @throws \Magento\Framework\Exception\LocalizedException
2461+ * @throws LocalizedException
2462 *
2463 * @return void
2464 *
2465@@ -28,9 +116,49 @@ class Full extends \Magento\CatalogInventory\Model\Indexer\Stock\AbstractAction
2466 public function execute($ids = null)
2467 {
2468 try {
2469- $this->reindexAll();
2470+ $this->useIdxTable(false);
2471+ $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
2472+
2473+ $columns = array_keys($this->_getConnection()->describeTable($this->_getIdxTable()));
2474+
2475+ /** @var \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock $indexer */
2476+ foreach ($this->_getTypeIndexers() as $indexer) {
2477+ $indexer->setActionType(self::ACTION_TYPE);
2478+ $connection = $indexer->getConnection();
2479+ $tableName = $this->activeTableSwitcher->getAdditionalTableName($indexer->getMainTable());
2480+
2481+ $batchRowCount = isset($this->batchRowsCount[$indexer->getTypeId()])
2482+ ? $this->batchRowsCount[$indexer->getTypeId()]
2483+ : $this->batchRowsCount['default'];
2484+
2485+ $this->batchSizeManagement->ensureBatchSize($connection, $batchRowCount);
2486+ $batches = $this->batchProvider->getBatches(
2487+ $connection,
2488+ $entityMetadata->getEntityTable(),
2489+ $entityMetadata->getIdentifierField(),
2490+ $batchRowCount
2491+ );
2492+
2493+ foreach ($batches as $batch) {
2494+ $this->clearTemporaryIndexTable();
2495+ // Get entity ids from batch
2496+ $select = $connection->select();
2497+ $select->distinct(true);
2498+ $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField());
2499+ $select->where('type_id = ?', $indexer->getTypeId());
2500+
2501+ $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch);
2502+ if (!empty($entityIds)) {
2503+ $indexer->reindexEntity($entityIds);
2504+ $select = $connection->select()->from($this->_getIdxTable(), $columns);
2505+ $query = $select->insertFromSelect($tableName, $columns);
2506+ $connection->query($query);
2507+ }
2508+ }
2509+ }
2510+ $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]);
2511 } catch (\Exception $e) {
2512- throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
2513+ throw new LocalizedException(__($e->getMessage()), $e);
2514 }
2515 }
2516 }
2517diff --git a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
2518index fcac88be425..ba9960a6b9c 100644
2519--- a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
2520+++ b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
2521@@ -10,6 +10,7 @@ use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer;
2522 use Magento\CatalogInventory\Model\Stock;
2523 use Magento\Framework\DB\Adapter\AdapterInterface;
2524 use Magento\CatalogInventory\Api\StockConfigurationInterface;
2525+use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
2526
2527 /**
2528 * CatalogInventory Default Stock Status Indexer Resource Model
2529@@ -48,6 +49,13 @@ class DefaultStock extends AbstractIndexer implements StockInterface
2530 */
2531 protected $stockConfiguration;
2532
2533+ /**
2534+ * Param for switching logic which depends on action type (full reindex or partial)
2535+ *
2536+ * @var string
2537+ */
2538+ private $actionType;
2539+
2540 /**
2541 * Class constructor
2542 *
2543@@ -106,10 +114,38 @@ class DefaultStock extends AbstractIndexer implements StockInterface
2544 */
2545 public function reindexEntity($entityIds)
2546 {
2547+ if ($this->getActionType() === Full::ACTION_TYPE) {
2548+ $this->tableStrategy->setUseIdxTable(false);
2549+ $this->_prepareIndexTable($entityIds);
2550+ return $this;
2551+ }
2552+
2553 $this->_updateIndex($entityIds);
2554 return $this;
2555 }
2556
2557+ /**
2558+ * Returns action run type
2559+ *
2560+ * @return string
2561+ */
2562+ public function getActionType()
2563+ {
2564+ return $this->actionType;
2565+ }
2566+
2567+ /**
2568+ * Set action run type
2569+ *
2570+ * @param string $type
2571+ * @return $this
2572+ */
2573+ public function setActionType($type)
2574+ {
2575+ $this->actionType = $type;
2576+ return $this;
2577+ }
2578+
2579 /**
2580 * Set active Product Type Id
2581 *
2582@@ -221,7 +257,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface
2583 protected function _prepareIndexTable($entityIds = null)
2584 {
2585 $connection = $this->getConnection();
2586- $select = $this->_getStockStatusSelect($entityIds);
2587+ $select = $this->_getStockStatusSelect($entityIds, true);
2588 $select = $this->getQueryProcessorComposite()->processQuery($select, $entityIds);
2589 $query = $select->insertFromSelect($this->getIdxTable());
2590 $connection->query($query);
2591diff --git a/vendor/magento/module-catalog-inventory/Setup/UpgradeSchema.php b/vendor/magento/module-catalog-inventory/Setup/UpgradeSchema.php
2592new file mode 100644
2593index 00000000000..f1975841f1d
2594--- /dev/null
2595+++ b/vendor/magento/module-catalog-inventory/Setup/UpgradeSchema.php
2596@@ -0,0 +1,46 @@
2597+<?php
2598+/**
2599+ * Copyright © 2013-2017 Magento, Inc. All rights reserved.
2600+ * See COPYING.txt for license details.
2601+ */
2602+
2603+namespace Magento\CatalogInventory\Setup;
2604+
2605+use Magento\Framework\Setup\UpgradeSchemaInterface;
2606+use Magento\Framework\Setup\ModuleContextInterface;
2607+use Magento\Framework\Setup\SchemaSetupInterface;
2608+
2609+class UpgradeSchema implements UpgradeSchemaInterface
2610+{
2611+ /**
2612+ * {@inheritdoc}
2613+ */
2614+ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
2615+ {
2616+ $setup->startSetup();
2617+
2618+ if (version_compare($context->getVersion(), '2.0.1.1.2', '<')) {
2619+ $this->addReplicaTable($setup, 'cataloginventory_stock_status', 'cataloginventory_stock_status_replica');
2620+ }
2621+
2622+ $setup->endSetup();
2623+ }
2624+
2625+ /**
2626+ * Add replica table for existing one.
2627+ *
2628+ * @param SchemaSetupInterface $setup
2629+ * @param string $existingTable
2630+ * @param string $replicaTable
2631+ * @return void
2632+ */
2633+ private function addReplicaTable(SchemaSetupInterface $setup, $existingTable, $replicaTable)
2634+ {
2635+ $sql = sprintf(
2636+ 'CREATE TABLE IF NOT EXISTS %s LIKE %s',
2637+ $setup->getConnection()->quoteIdentifier($setup->getTable($replicaTable)),
2638+ $setup->getConnection()->quoteIdentifier($setup->getTable($existingTable))
2639+ );
2640+ $setup->getConnection()->query($sql);
2641+ }
2642+}
2643diff --git a/vendor/magento/module-catalog-inventory/etc/di.xml b/vendor/magento/module-catalog-inventory/etc/di.xml
2644index 9e970cef7d0..10a72edb0db 100644
2645--- a/vendor/magento/module-catalog-inventory/etc/di.xml
2646+++ b/vendor/magento/module-catalog-inventory/etc/di.xml
2647@@ -86,4 +86,22 @@
2648 <argument name="indexerProcessor" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\Processor</argument>
2649 </arguments>
2650 </type>
2651+ <virtualType name="Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement" type="Magento\Framework\Indexer\BatchSizeManagement">
2652+ <arguments>
2653+ <argument name="rowSizeEstimator" xsi:type="object">Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\IndexTableRowSizeEstimator</argument>
2654+ </arguments>
2655+ </virtualType>
2656+ <virtualType name="Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\IndexTableRowSizeEstimator" type="Magento\Framework\Indexer\IndexTableRowSizeEstimator">
2657+ <arguments>
2658+ <argument name="rowMemorySize" xsi:type="number">100</argument>
2659+ </arguments>
2660+ </virtualType>
2661+ <type name="Magento\CatalogInventory\Model\Indexer\Stock\Action\Full">
2662+ <arguments>
2663+ <argument name="batchRowsCount" xsi:type="array">
2664+ <item name="default" xsi:type="number">200</item>
2665+ </argument>
2666+ <argument name="batchSizeManagement" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement</argument>
2667+ </arguments>
2668+ </type>
2669 </config>
2670diff --git a/vendor/magento/module-catalog-inventory/etc/module.xml b/vendor/magento/module-catalog-inventory/etc/module.xml
2671index 6d86fa55984..676d3e8ef82 100644
2672--- a/vendor/magento/module-catalog-inventory/etc/module.xml
2673+++ b/vendor/magento/module-catalog-inventory/etc/module.xml
2674@@ -6,7 +6,7 @@
2675 */
2676 -->
2677 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
2678- <module name="Magento_CatalogInventory" setup_version="2.0.1">
2679+ <module name="Magento_CatalogInventory" setup_version="2.0.1.1.2">
2680 <sequence>
2681 <module name="Magento_Catalog"/>
2682 </sequence>
2683diff --git a/vendor/magento/module-catalog-rule/Api/IndexerTableSwapperInterface.php b/vendor/magento/module-catalog-rule/Api/IndexerTableSwapperInterface.php
2684new file mode 100644
2685index 00000000000..664505c48ba
2686--- /dev/null
2687+++ b/vendor/magento/module-catalog-rule/Api/IndexerTableSwapperInterface.php
2688@@ -0,0 +1,32 @@
2689+<?php
2690+/**
2691+ * Copyright © Magento, Inc. All rights reserved.
2692+ * See COPYING.txt for license details.
2693+ */
2694+
2695+namespace Magento\CatalogRule\Api;
2696+
2697+/**
2698+ * Manage additional tables used while building new index to preserve
2699+ * index tables until the process finishes.
2700+ */
2701+interface IndexerTableSwapperInterface
2702+{
2703+ /**
2704+ * Get working table name used to build index.
2705+ *
2706+ * @param string $originalTable
2707+ *
2708+ * @return string
2709+ */
2710+ public function getWorkingTableName(string $originalTable): string;
2711+
2712+ /**
2713+ * Swap working tables with actual tables to save new indexes.
2714+ *
2715+ * @param string[] $originalTablesNames
2716+ *
2717+ * @return void
2718+ */
2719+ public function swapIndexTables(array $originalTablesNames);
2720+}
2721diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/IndexBuilder.php b/vendor/magento/module-catalog-rule/Model/Indexer/IndexBuilder.php
2722index 423aaa6986e..af17aa7efbc 100644
2723--- a/vendor/magento/module-catalog-rule/Model/Indexer/IndexBuilder.php
2724+++ b/vendor/magento/module-catalog-rule/Model/Indexer/IndexBuilder.php
2725@@ -6,16 +6,17 @@
2726
2727 namespace Magento\CatalogRule\Model\Indexer;
2728
2729-use Magento\Catalog\Api\Data\ProductInterface;
2730 use Magento\Catalog\Model\Product;
2731 use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory;
2732 use Magento\CatalogRule\Model\Rule;
2733-use Magento\Framework\App\ResourceConnection;
2734-use Magento\Framework\EntityManager\MetadataPool;
2735+use Magento\Framework\App\ObjectManager;
2736 use Magento\Framework\Pricing\PriceCurrencyInterface;
2737+use Magento\CatalogRule\Api\IndexerTableSwapperInterface as TableSwapper;
2738
2739 /**
2740 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2741+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
2742+ * @SuppressWarnings(PHPMD.TooManyFields)
2743 */
2744 class IndexBuilder
2745 {
2746@@ -23,6 +24,7 @@ class IndexBuilder
2747
2748 /**
2749 * @var \Magento\Framework\EntityManager\MetadataPool
2750+ * @deprecated
2751 */
2752 protected $metadataPool;
2753
2754@@ -32,6 +34,7 @@ class IndexBuilder
2755 * This array contain list of CatalogRuleGroupWebsite table columns
2756 *
2757 * @var array
2758+ * @deprecated
2759 */
2760 protected $_catalogRuleGroupWebsiteColumnsList = ['rule_id', 'customer_group_id', 'website_id'];
2761
2762@@ -95,6 +98,46 @@ class IndexBuilder
2763 */
2764 protected $connection;
2765
2766+ /**
2767+ * @var \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator
2768+ */
2769+ private $productPriceCalculator;
2770+
2771+ /**
2772+ * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct
2773+ */
2774+ private $reindexRuleProduct;
2775+
2776+ /**
2777+ * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite
2778+ */
2779+ private $reindexRuleGroupWebsite;
2780+
2781+ /**
2782+ * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder
2783+ */
2784+ private $ruleProductsSelectBuilder;
2785+
2786+ /**
2787+ * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice
2788+ */
2789+ private $reindexRuleProductPrice;
2790+
2791+ /**
2792+ * @var \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor
2793+ */
2794+ private $pricesPersistor;
2795+
2796+ /**
2797+ * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
2798+ */
2799+ private $activeTableSwitcher;
2800+
2801+ /**
2802+ * @var TableSwapper
2803+ */
2804+ private $tableSwapper;
2805+
2806 /**
2807 * @param RuleCollectionFactory $ruleCollectionFactory
2808 * @param PriceCurrencyInterface $priceCurrency
2809@@ -106,7 +149,16 @@ class IndexBuilder
2810 * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
2811 * @param \Magento\Catalog\Model\ProductFactory $productFactory
2812 * @param int $batchCount
2813+ * @param \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator|null $productPriceCalculator
2814+ * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct|null $reindexRuleProduct
2815+ * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite|null $reindexRuleGroupWebsite
2816+ * @param \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder|null $ruleProductsSelectBuilder
2817+ * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice|null $reindexRuleProductPrice
2818+ * @param \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor|null $pricesPersistor
2819+ * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
2820+ * @param TableSwapper|null $tableSwapper
2821 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
2822+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
2823 */
2824 public function __construct(
2825 RuleCollectionFactory $ruleCollectionFactory,
2826@@ -118,7 +170,15 @@ class IndexBuilder
2827 \Magento\Framework\Stdlib\DateTime $dateFormat,
2828 \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
2829 \Magento\Catalog\Model\ProductFactory $productFactory,
2830- $batchCount = 1000
2831+ $batchCount = 1000,
2832+ \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator $productPriceCalculator = null,
2833+ \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct $reindexRuleProduct = null,
2834+ \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite $reindexRuleGroupWebsite = null,
2835+ \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder $ruleProductsSelectBuilder = null,
2836+ \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice $reindexRuleProductPrice = null,
2837+ \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor = null,
2838+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null,
2839+ TableSwapper $tableSwapper = null
2840 ) {
2841 $this->resource = $resource;
2842 $this->connection = $resource->getConnection();
2843@@ -131,6 +191,30 @@ class IndexBuilder
2844 $this->dateTime = $dateTime;
2845 $this->productFactory = $productFactory;
2846 $this->batchCount = $batchCount;
2847+
2848+ $this->productPriceCalculator = $productPriceCalculator ?: ObjectManager::getInstance()->get(
2849+ \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::class
2850+ );
2851+ $this->reindexRuleProduct = $reindexRuleProduct ?: ObjectManager::getInstance()->get(
2852+ \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct::class
2853+ );
2854+ $this->reindexRuleGroupWebsite = $reindexRuleGroupWebsite ?: ObjectManager::getInstance()->get(
2855+ \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class
2856+ );
2857+ $this->ruleProductsSelectBuilder = $ruleProductsSelectBuilder ?: ObjectManager::getInstance()->get(
2858+ \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::class
2859+ );
2860+ $this->reindexRuleProductPrice = $reindexRuleProductPrice ?: ObjectManager::getInstance()->get(
2861+ \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class
2862+ );
2863+ $this->pricesPersistor = $pricesPersistor ?: ObjectManager::getInstance()->get(
2864+ \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::class
2865+ );
2866+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get(
2867+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class
2868+ );
2869+
2870+ $this->tableSwapper = $tableSwapper ?: ObjectManager::getInstance()->get(TableSwapper::class);
2871 }
2872
2873 /**
2874@@ -207,9 +291,19 @@ class IndexBuilder
2875 protected function doReindexFull()
2876 {
2877 foreach ($this->getAllRules() as $rule) {
2878- $this->updateRuleProductData($rule);
2879+ $this->reindexRuleProduct->execute($rule, $this->batchCount, true);
2880 }
2881- $this->deleteOldData()->applyAllRules();
2882+
2883+ $this->reindexRuleProductPrice->execute($this->batchCount, null, true);
2884+ $this->reindexRuleGroupWebsite->execute(true);
2885+
2886+ $this->tableSwapper->swapIndexTables(
2887+ [
2888+ $this->getTable('catalogrule_product'),
2889+ $this->getTable('catalogrule_product_price'),
2890+ $this->getTable('catalogrule_group_website')
2891+ ]
2892+ );
2893 }
2894
2895 /**
2896@@ -305,7 +399,8 @@ class IndexBuilder
2897 throw $e;
2898 }
2899
2900- $this->applyAllRules($product);
2901+ $this->reindexRuleProductPrice->execute($this->batchCount, $product);
2902+ $this->reindexRuleGroupWebsite->execute();
2903
2904 return $this;
2905 }
2906@@ -322,8 +417,8 @@ class IndexBuilder
2907 /**
2908 * @param Rule $rule
2909 * @return $this
2910- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
2911- * @SuppressWarnings(PHPMD.NPathComplexity)
2912+ * @deprecated
2913+ * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct::execute
2914 */
2915 protected function updateRuleProductData(Rule $rule)
2916 {
2917@@ -340,63 +435,7 @@ class IndexBuilder
2918 );
2919 }
2920
2921- if (!$rule->getIsActive()) {
2922- return $this;
2923- }
2924-
2925- $websiteIds = $rule->getWebsiteIds();
2926- if (!is_array($websiteIds)) {
2927- $websiteIds = explode(',', $websiteIds);
2928- }
2929- if (empty($websiteIds)) {
2930- return $this;
2931- }
2932-
2933- \Magento\Framework\Profiler::start('__MATCH_PRODUCTS__');
2934- $productIds = $rule->getMatchingProductIds();
2935- \Magento\Framework\Profiler::stop('__MATCH_PRODUCTS__');
2936-
2937- $customerGroupIds = $rule->getCustomerGroupIds();
2938- $fromTime = strtotime($rule->getFromDate());
2939- $toTime = strtotime($rule->getToDate());
2940- $toTime = $toTime ? $toTime + self::SECONDS_IN_DAY - 1 : 0;
2941- $sortOrder = (int)$rule->getSortOrder();
2942- $actionOperator = $rule->getSimpleAction();
2943- $actionAmount = $rule->getDiscountAmount();
2944- $actionStop = $rule->getStopRulesProcessing();
2945-
2946- $rows = [];
2947-
2948- foreach ($productIds as $productId => $validationByWebsite) {
2949- foreach ($websiteIds as $websiteId) {
2950- if (empty($validationByWebsite[$websiteId])) {
2951- continue;
2952- }
2953- foreach ($customerGroupIds as $customerGroupId) {
2954- $rows[] = [
2955- 'rule_id' => $ruleId,
2956- 'from_time' => $fromTime,
2957- 'to_time' => $toTime,
2958- 'website_id' => $websiteId,
2959- 'customer_group_id' => $customerGroupId,
2960- 'product_id' => $productId,
2961- 'action_operator' => $actionOperator,
2962- 'action_amount' => $actionAmount,
2963- 'action_stop' => $actionStop,
2964- 'sort_order' => $sortOrder,
2965- ];
2966-
2967- if (count($rows) == $this->batchCount) {
2968- $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows);
2969- $rows = [];
2970- }
2971- }
2972- }
2973- }
2974- if (!empty($rows)) {
2975- $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows);
2976- }
2977-
2978+ $this->reindexRuleProduct->execute($rule, $this->batchCount);
2979 return $this;
2980 }
2981
2982@@ -404,123 +443,27 @@ class IndexBuilder
2983 * @param Product|null $product
2984 * @throws \Exception
2985 * @return $this
2986- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
2987- * @SuppressWarnings(PHPMD.NPathComplexity)
2988- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
2989+ * @deprecated
2990+ * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::execute
2991+ * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::execute
2992 */
2993 protected function applyAllRules(Product $product = null)
2994 {
2995- $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1);
2996- $toDate = mktime(0, 0, 0, date('m'), date('d') + 1);
2997-
2998- /**
2999- * Update products rules prices per each website separately
3000- * because of max join limit in mysql
3001- */
3002- foreach ($this->storeManager->getWebsites() as $website) {
3003- $productsStmt = $this->getRuleProductsStmt($website->getId(), $product);
3004-
3005- $dayPrices = [];
3006- $stopFlags = [];
3007- $prevKey = null;
3008-
3009- while ($ruleData = $productsStmt->fetch()) {
3010- $ruleProductId = $ruleData['product_id'];
3011- $productKey = $ruleProductId .
3012- '_' .
3013- $ruleData['website_id'] .
3014- '_' .
3015- $ruleData['customer_group_id'];
3016-
3017- if ($prevKey && $prevKey != $productKey) {
3018- $stopFlags = [];
3019- if (count($dayPrices) > $this->batchCount) {
3020- $this->saveRuleProductPrices($dayPrices);
3021- $dayPrices = [];
3022- }
3023- }
3024-
3025- $ruleData['from_time'] = $this->roundTime($ruleData['from_time']);
3026- $ruleData['to_time'] = $this->roundTime($ruleData['to_time']);
3027- /**
3028- * Build prices for each day
3029- */
3030- for ($time = $fromDate; $time <= $toDate; $time += self::SECONDS_IN_DAY) {
3031- if (($ruleData['from_time'] == 0 ||
3032- $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 ||
3033- $time <= $ruleData['to_time'])
3034- ) {
3035- $priceKey = $time . '_' . $productKey;
3036-
3037- if (isset($stopFlags[$priceKey])) {
3038- continue;
3039- }
3040-
3041- if (!isset($dayPrices[$priceKey])) {
3042- $dayPrices[$priceKey] = [
3043- 'rule_date' => $time,
3044- 'website_id' => $ruleData['website_id'],
3045- 'customer_group_id' => $ruleData['customer_group_id'],
3046- 'product_id' => $ruleProductId,
3047- 'rule_price' => $this->calcRuleProductPrice($ruleData),
3048- 'latest_start_date' => $ruleData['from_time'],
3049- 'earliest_end_date' => $ruleData['to_time'],
3050- ];
3051- } else {
3052- $dayPrices[$priceKey]['rule_price'] = $this->calcRuleProductPrice(
3053- $ruleData,
3054- $dayPrices[$priceKey]
3055- );
3056- $dayPrices[$priceKey]['latest_start_date'] = max(
3057- $dayPrices[$priceKey]['latest_start_date'],
3058- $ruleData['from_time']
3059- );
3060- $dayPrices[$priceKey]['earliest_end_date'] = min(
3061- $dayPrices[$priceKey]['earliest_end_date'],
3062- $ruleData['to_time']
3063- );
3064- }
3065-
3066- if ($ruleData['action_stop']) {
3067- $stopFlags[$priceKey] = true;
3068- }
3069- }
3070- }
3071-
3072- $prevKey = $productKey;
3073- }
3074- $this->saveRuleProductPrices($dayPrices);
3075- }
3076-
3077- return $this->updateCatalogRuleGroupWebsiteData();
3078+ $this->reindexRuleProductPrice->execute($this->batchCount, $product);
3079+ $this->reindexRuleGroupWebsite->execute();
3080+ return $this;
3081 }
3082
3083 /**
3084 * Update CatalogRuleGroupWebsite data
3085 *
3086 * @return $this
3087+ * @deprecated
3088+ * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::execute
3089 */
3090 protected function updateCatalogRuleGroupWebsiteData()
3091 {
3092- $this->connection->delete($this->getTable('catalogrule_group_website'), []);
3093-
3094- $timestamp = $this->dateTime->gmtTimestamp();
3095-
3096- $select = $this->connection->select()->distinct(
3097- true
3098- )->from(
3099- $this->getTable('catalogrule_product'),
3100- $this->_catalogRuleGroupWebsiteColumnsList
3101- )->where(
3102- "{$timestamp} >= from_time AND (({$timestamp} <= to_time AND to_time > 0) OR to_time = 0)"
3103- );
3104- $query = $select->insertFromSelect(
3105- $this->getTable('catalogrule_group_website'),
3106- $this->_catalogRuleGroupWebsiteColumnsList
3107- );
3108-
3109- $this->connection->query($query);
3110-
3111+ $this->reindexRuleGroupWebsite->execute();
3112 return $this;
3113 }
3114
3115@@ -539,33 +482,12 @@ class IndexBuilder
3116 * @param array $ruleData
3117 * @param null $productData
3118 * @return float
3119+ * @deprecated
3120+ * @see \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::calculate
3121 */
3122 protected function calcRuleProductPrice($ruleData, $productData = null)
3123 {
3124- if ($productData !== null && isset($productData['rule_price'])) {
3125- $productPrice = $productData['rule_price'];
3126- } else {
3127- $productPrice = $ruleData['default_price'];
3128- }
3129-
3130- switch ($ruleData['action_operator']) {
3131- case 'to_fixed':
3132- $productPrice = min($ruleData['action_amount'], $productPrice);
3133- break;
3134- case 'to_percent':
3135- $productPrice = $productPrice * $ruleData['action_amount'] / 100;
3136- break;
3137- case 'by_fixed':
3138- $productPrice = max(0, $productPrice - $ruleData['action_amount']);
3139- break;
3140- case 'by_percent':
3141- $productPrice = $productPrice * (1 - $ruleData['action_amount'] / 100);
3142- break;
3143- default:
3144- $productPrice = 0;
3145- }
3146-
3147- return $this->priceCurrency->round($productPrice);
3148+ return $this->productPriceCalculator->calculate($ruleData, $productData);
3149 }
3150
3151 /**
3152@@ -573,107 +495,24 @@ class IndexBuilder
3153 * @param Product|null $product
3154 * @return \Zend_Db_Statement_Interface
3155 * @throws \Magento\Framework\Exception\LocalizedException
3156+ * @deprecated
3157+ * @see \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::build
3158 */
3159 protected function getRuleProductsStmt($websiteId, Product $product = null)
3160 {
3161- /**
3162- * Sort order is important
3163- * It used for check stop price rule condition.
3164- * website_id customer_group_id product_id sort_order
3165- * 1 1 1 0
3166- * 1 1 1 1
3167- * 1 1 1 2
3168- * if row with sort order 1 will have stop flag we should exclude
3169- * all next rows for same product id from price calculation
3170- */
3171- $select = $this->connection->select()->from(
3172- ['rp' => $this->getTable('catalogrule_product')]
3173- )->order(
3174- ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id']
3175- );
3176-
3177- if ($product && $product->getEntityId()) {
3178- $select->where('rp.product_id=?', $product->getEntityId());
3179- }
3180-
3181- /**
3182- * Join default price and websites prices to result
3183- */
3184- $priceAttr = $this->eavConfig->getAttribute(Product::ENTITY, 'price');
3185- $priceTable = $priceAttr->getBackend()->getTable();
3186- $attributeId = $priceAttr->getId();
3187-
3188- $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
3189- $select->join(
3190- ['e' => $this->getTable('catalog_product_entity')],
3191- sprintf('e.entity_id = rp.product_id'),
3192- []
3193- );
3194- $joinCondition = '%1$s.' . $linkField . '=e.' . $linkField . ' AND (%1$s.attribute_id='
3195- . $attributeId
3196- . ') and %1$s.store_id=%2$s';
3197-
3198- $select->join(
3199- ['pp_default' => $priceTable],
3200- sprintf($joinCondition, 'pp_default', \Magento\Store\Model\Store::DEFAULT_STORE_ID),
3201- []
3202- );
3203-
3204- $website = $this->storeManager->getWebsite($websiteId);
3205- $defaultGroup = $website->getDefaultGroup();
3206- if ($defaultGroup instanceof \Magento\Store\Model\Group) {
3207- $storeId = $defaultGroup->getDefaultStoreId();
3208- } else {
3209- $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID;
3210- }
3211-
3212- $select->joinInner(
3213- ['product_website' => $this->getTable('catalog_product_website')],
3214- 'product_website.product_id=rp.product_id '
3215- . 'AND product_website.website_id = rp.website_id '
3216- . 'AND product_website.website_id='
3217- . $websiteId,
3218- []
3219- );
3220-
3221- $tableAlias = 'pp' . $websiteId;
3222- $select->joinLeft(
3223- [$tableAlias => $priceTable],
3224- sprintf($joinCondition, $tableAlias, $storeId),
3225- []
3226- );
3227- $select->columns([
3228- 'default_price' =>$this->connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'),
3229- ]);
3230-
3231- return $this->connection->query($select);
3232+ return $this->ruleProductsSelectBuilder->build($websiteId, $product);
3233 }
3234
3235 /**
3236 * @param array $arrData
3237 * @return $this
3238 * @throws \Exception
3239+ * @deprecated
3240+ * @see \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::execute
3241 */
3242 protected function saveRuleProductPrices($arrData)
3243 {
3244- if (empty($arrData)) {
3245- return $this;
3246- }
3247-
3248- $productIds = [];
3249-
3250- try {
3251- foreach ($arrData as $key => $data) {
3252- $productIds['product_id'] = $data['product_id'];
3253- $arrData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false);
3254- $arrData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false);
3255- $arrData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false);
3256- }
3257- $this->connection->insertOnDuplicate($this->getTable('catalogrule_product_price'), $arrData);
3258- } catch (\Exception $e) {
3259- throw $e;
3260- }
3261-
3262+ $this->pricesPersistor->execute($arrData);
3263 return $this;
3264 }
3265
3266@@ -684,8 +523,7 @@ class IndexBuilder
3267 */
3268 protected function getActiveRules()
3269 {
3270- return $this->ruleCollectionFactory->create()
3271- ->addFieldToFilter('is_active', 1);
3272+ return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 1);
3273 }
3274
3275 /**
3276@@ -722,6 +560,7 @@ class IndexBuilder
3277 /**
3278 * @param int $timeStamp
3279 * @return int
3280+ * @deprecated
3281 */
3282 private function roundTime($timeStamp)
3283 {
3284@@ -734,6 +573,7 @@ class IndexBuilder
3285
3286 /**
3287 * @return MetadataPool
3288+ * @deprecated
3289 */
3290 private function getMetadataPool()
3291 {
3292diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/IndexerTableSwapper.php b/vendor/magento/module-catalog-rule/Model/Indexer/IndexerTableSwapper.php
3293new file mode 100644
3294index 00000000000..e2e53f724a7
3295--- /dev/null
3296+++ b/vendor/magento/module-catalog-rule/Model/Indexer/IndexerTableSwapper.php
3297@@ -0,0 +1,127 @@
3298+<?php
3299+/**
3300+ * Copyright © Magento, Inc. All rights reserved.
3301+ * See COPYING.txt for license details.
3302+ */
3303+
3304+namespace Magento\CatalogRule\Model\Indexer;
3305+
3306+use Magento\CatalogRule\Api\IndexerTableSwapperInterface;
3307+use Magento\Framework\App\ResourceConnection;
3308+
3309+/**
3310+ * @inheritDoc
3311+ */
3312+class IndexerTableSwapper implements IndexerTableSwapperInterface
3313+{
3314+ /**
3315+ * Keys are original tables' names, values - created temporary tables.
3316+ *
3317+ * @var string[]
3318+ */
3319+ private $temporaryTables = [];
3320+
3321+ /**
3322+ * @var ResourceConnection
3323+ */
3324+ private $resourceConnection;
3325+
3326+ /**
3327+ * @param ResourceConnection $resource
3328+ */
3329+ public function __construct(ResourceConnection $resource)
3330+ {
3331+ $this->resourceConnection = $resource;
3332+ }
3333+
3334+ /**
3335+ * Create temporary table based on given table to use instead of original.
3336+ *
3337+ * @param string $originalTableName
3338+ *
3339+ * @return string Created table name.
3340+ * @throws \Throwable
3341+ */
3342+ private function createTemporaryTable(string $originalTableName): string
3343+ {
3344+ $temporaryTableName = $this->resourceConnection->getTableName(
3345+ $originalTableName . '__temp' . $this->generateRandomSuffix()
3346+ );
3347+
3348+ $this->resourceConnection->getConnection()->query(
3349+ sprintf(
3350+ 'create table %s like %s',
3351+ $temporaryTableName,
3352+ $this->resourceConnection->getTableName($originalTableName)
3353+ )
3354+ );
3355+
3356+ return $temporaryTableName;
3357+ }
3358+
3359+ /**
3360+ * Random suffix for temporary tables not to conflict with each other.
3361+ *
3362+ * @return string
3363+ */
3364+ private function generateRandomSuffix(): string
3365+ {
3366+ return bin2hex(random_bytes(4));
3367+ }
3368+
3369+ /**
3370+ * @inheritDoc
3371+ */
3372+ public function getWorkingTableName(string $originalTable): string
3373+ {
3374+ $originalTable = $this->resourceConnection->getTableName($originalTable);
3375+ if (!array_key_exists($originalTable, $this->temporaryTables)) {
3376+ $this->temporaryTables[$originalTable]
3377+ = $this->createTemporaryTable($originalTable);
3378+ }
3379+
3380+ return $this->temporaryTables[$originalTable];
3381+ }
3382+
3383+ /**
3384+ * @inheritDoc
3385+ */
3386+ public function swapIndexTables(array $originalTablesNames)
3387+ {
3388+ $toRename = [];
3389+ /** @var string[] $toDrop */
3390+ $toDrop = [];
3391+ /** @var string[] $temporaryTablesRenamed */
3392+ $temporaryTablesRenamed = [];
3393+ //Renaming temporary tables to original tables' names, dropping old
3394+ //tables.
3395+ foreach ($originalTablesNames as $tableName) {
3396+ $tableName = $this->resourceConnection->getTableName($tableName);
3397+ $temporaryOriginalName = $this->resourceConnection->getTableName(
3398+ $tableName . $this->generateRandomSuffix()
3399+ );
3400+ $temporaryTableName = $this->getWorkingTableName($tableName);
3401+ $toRename[] = [
3402+ 'oldName' => $tableName,
3403+ 'newName' => $temporaryOriginalName
3404+ ];
3405+ $toRename[] = [
3406+ 'oldName' => $temporaryTableName,
3407+ 'newName' => $tableName
3408+ ];
3409+ $toDrop[] = $temporaryOriginalName;
3410+ $temporaryTablesRenamed[] = $tableName;
3411+ }
3412+
3413+ //Swapping tables.
3414+ $this->resourceConnection->getConnection()->renameTablesBatch($toRename);
3415+ //Cleaning up.
3416+ foreach ($temporaryTablesRenamed as $tableName) {
3417+ unset($this->temporaryTables[$tableName]);
3418+ }
3419+ //Removing old ones.
3420+ foreach ($toDrop as $tableName) {
3421+ $this->resourceConnection->getConnection()->dropTable($tableName);
3422+ }
3423+ }
3424+}
3425diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/ProductPriceCalculator.php b/vendor/magento/module-catalog-rule/Model/Indexer/ProductPriceCalculator.php
3426new file mode 100644
3427index 00000000000..02b499c75b7
3428--- /dev/null
3429+++ b/vendor/magento/module-catalog-rule/Model/Indexer/ProductPriceCalculator.php
3430@@ -0,0 +1,61 @@
3431+<?php
3432+/**
3433+ * Copyright © Magento, Inc. All rights reserved.
3434+ * See COPYING.txt for license details.
3435+ */
3436+
3437+namespace Magento\CatalogRule\Model\Indexer;
3438+
3439+/**
3440+ * Product price calculation according rules settings.
3441+ */
3442+class ProductPriceCalculator
3443+{
3444+ /**
3445+ * @var \Magento\Framework\Pricing\PriceCurrencyInterface
3446+ */
3447+ private $priceCurrency;
3448+
3449+ /**
3450+ * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
3451+ */
3452+ public function __construct(\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency)
3453+ {
3454+ $this->priceCurrency = $priceCurrency;
3455+ }
3456+
3457+ /**
3458+ * Calculates product price.
3459+ *
3460+ * @param array $ruleData
3461+ * @param null $productData
3462+ * @return float
3463+ */
3464+ public function calculate($ruleData, $productData = null)
3465+ {
3466+ if ($productData !== null && isset($productData['rule_price'])) {
3467+ $productPrice = $productData['rule_price'];
3468+ } else {
3469+ $productPrice = $ruleData['default_price'];
3470+ }
3471+
3472+ switch ($ruleData['action_operator']) {
3473+ case 'to_fixed':
3474+ $productPrice = min($ruleData['action_amount'], $productPrice);
3475+ break;
3476+ case 'to_percent':
3477+ $productPrice = $productPrice * $ruleData['action_amount'] / 100;
3478+ break;
3479+ case 'by_fixed':
3480+ $productPrice = max(0, $productPrice - $ruleData['action_amount']);
3481+ break;
3482+ case 'by_percent':
3483+ $productPrice = $productPrice * (1 - $ruleData['action_amount'] / 100);
3484+ break;
3485+ default:
3486+ $productPrice = 0;
3487+ }
3488+
3489+ return $this->priceCurrency->round($productPrice);
3490+ }
3491+}
3492diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleGroupWebsite.php b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleGroupWebsite.php
3493new file mode 100644
3494index 00000000000..482aaf4834d
3495--- /dev/null
3496+++ b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleGroupWebsite.php
3497@@ -0,0 +1,92 @@
3498+<?php
3499+/**
3500+ * Copyright © Magento, Inc. All rights reserved.
3501+ * See COPYING.txt for license details.
3502+ */
3503+
3504+namespace Magento\CatalogRule\Model\Indexer;
3505+
3506+use Magento\CatalogRule\Api\IndexerTableSwapperInterface as TableSwapper;
3507+use Magento\Framework\App\ObjectManager;
3508+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
3509+
3510+/**
3511+ * Reindex information about rule relations with customer groups and websites.
3512+ */
3513+class ReindexRuleGroupWebsite
3514+{
3515+ /**
3516+ * @var \Magento\Framework\Stdlib\DateTime\DateTime
3517+ */
3518+ private $dateTime;
3519+
3520+ /**
3521+ * @var \Magento\Framework\App\ResourceConnection
3522+ */
3523+ private $resource;
3524+
3525+ /**
3526+ * @var array
3527+ */
3528+ private $catalogRuleGroupWebsiteColumnsList = ['rule_id', 'customer_group_id', 'website_id'];
3529+
3530+ /**
3531+ * @var TableSwapper
3532+ */
3533+ private $tableSwapper;
3534+
3535+ /**
3536+ * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
3537+ * @param \Magento\Framework\App\ResourceConnection $resource
3538+ * @param ActiveTableSwitcher $activeTableSwitcher
3539+ * @param TableSwapper|null $tableSwapper
3540+ *
3541+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
3542+ */
3543+ public function __construct(
3544+ \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
3545+ \Magento\Framework\App\ResourceConnection $resource,
3546+ ActiveTableSwitcher $activeTableSwitcher,
3547+ TableSwapper $tableSwapper = null
3548+ ) {
3549+ $this->dateTime = $dateTime;
3550+ $this->resource = $resource;
3551+ $this->tableSwapper = $tableSwapper ?: ObjectManager::getInstance()->get(TableSwapper::class);
3552+ }
3553+
3554+ /**
3555+ * Prepare and persist information about rule relations with customer groups and websites to index table.
3556+ *
3557+ * @param bool $useAdditionalTable
3558+ * @return bool
3559+ */
3560+ public function execute($useAdditionalTable = false)
3561+ {
3562+ $connection = $this->resource->getConnection();
3563+ $timestamp = $this->dateTime->gmtTimestamp();
3564+
3565+ $indexTable = $this->resource->getTableName('catalogrule_group_website');
3566+ $ruleProductTable = $this->resource->getTableName('catalogrule_product');
3567+ if ($useAdditionalTable) {
3568+ $indexTable = $this->resource->getTableName(
3569+ $this->tableSwapper->getWorkingTableName('catalogrule_group_website')
3570+ );
3571+ $ruleProductTable = $this->resource->getTableName(
3572+ $this->tableSwapper->getWorkingTableName('catalogrule_product')
3573+ );
3574+ }
3575+
3576+ $connection->delete($indexTable);
3577+ $select = $connection->select()->distinct(
3578+ true
3579+ )->from(
3580+ $ruleProductTable,
3581+ $this->catalogRuleGroupWebsiteColumnsList
3582+ )->where(
3583+ "{$timestamp} >= from_time AND (({$timestamp} <= to_time AND to_time > 0) OR to_time = 0)"
3584+ );
3585+ $query = $select->insertFromSelect($indexTable, $this->catalogRuleGroupWebsiteColumnsList);
3586+ $connection->query($query);
3587+ return true;
3588+ }
3589+}
3590diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProduct.php b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProduct.php
3591new file mode 100644
3592index 00000000000..f79f5aad6d6
3593--- /dev/null
3594+++ b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProduct.php
3595@@ -0,0 +1,123 @@
3596+<?php
3597+/**
3598+ * Copyright © Magento, Inc. All rights reserved.
3599+ * See COPYING.txt for license details.
3600+ */
3601+
3602+namespace Magento\CatalogRule\Model\Indexer;
3603+
3604+use Magento\CatalogRule\Api\IndexerTableSwapperInterface as TableSwapper;
3605+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
3606+use Magento\Framework\App\ObjectManager;
3607+
3608+/**
3609+ * Reindex rule relations with products.
3610+ */
3611+class ReindexRuleProduct
3612+{
3613+ /**
3614+ * @var \Magento\Framework\App\ResourceConnection
3615+ */
3616+ private $resource;
3617+
3618+ /**
3619+ * @var TableSwapper
3620+ */
3621+ private $tableSwapper;
3622+
3623+ /**
3624+ * @param \Magento\Framework\App\ResourceConnection $resource
3625+ * @param ActiveTableSwitcher $activeTableSwitcher
3626+ * @param TableSwapper|null $tableSwapper
3627+ *
3628+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
3629+ */
3630+ public function __construct(
3631+ \Magento\Framework\App\ResourceConnection $resource,
3632+ ActiveTableSwitcher $activeTableSwitcher,
3633+ TableSwapper $tableSwapper = null
3634+ ) {
3635+ $this->resource = $resource;
3636+ $this->tableSwapper = $tableSwapper ?: ObjectManager::getInstance()->get(TableSwapper::class);
3637+ }
3638+
3639+ /**
3640+ * Reindex information about rule relations with products.
3641+ *
3642+ * @param \Magento\CatalogRule\Model\Rule $rule
3643+ * @param int $batchCount
3644+ * @param bool $useAdditionalTable
3645+ * @return bool
3646+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
3647+ * @SuppressWarnings(PHPMD.NPathComplexity)
3648+ */
3649+ public function execute(
3650+ \Magento\CatalogRule\Model\Rule $rule,
3651+ $batchCount,
3652+ $useAdditionalTable = false
3653+ ) {
3654+ if (!$rule->getIsActive() || empty($rule->getWebsiteIds())) {
3655+ return false;
3656+ }
3657+
3658+ $connection = $this->resource->getConnection();
3659+ $websiteIds = $rule->getWebsiteIds();
3660+ if (!is_array($websiteIds)) {
3661+ $websiteIds = explode(',', $websiteIds);
3662+ }
3663+
3664+ \Magento\Framework\Profiler::start('__MATCH_PRODUCTS__');
3665+ $productIds = $rule->getMatchingProductIds();
3666+ \Magento\Framework\Profiler::stop('__MATCH_PRODUCTS__');
3667+
3668+ $indexTable = $this->resource->getTableName('catalogrule_product');
3669+ if ($useAdditionalTable) {
3670+ $indexTable = $this->resource->getTableName(
3671+ $this->tableSwapper->getWorkingTableName('catalogrule_product')
3672+ );
3673+ }
3674+
3675+ $ruleId = $rule->getId();
3676+ $customerGroupIds = $rule->getCustomerGroupIds();
3677+ $fromTime = strtotime($rule->getFromDate());
3678+ $toTime = strtotime($rule->getToDate());
3679+ $toTime = $toTime ? $toTime + \Magento\CatalogRule\Model\Indexer\IndexBuilder::SECONDS_IN_DAY - 1 : 0;
3680+ $sortOrder = (int)$rule->getSortOrder();
3681+ $actionOperator = $rule->getSimpleAction();
3682+ $actionAmount = $rule->getDiscountAmount();
3683+ $actionStop = $rule->getStopRulesProcessing();
3684+
3685+ $rows = [];
3686+
3687+ foreach ($productIds as $productId => $validationByWebsite) {
3688+ foreach ($websiteIds as $websiteId) {
3689+ if (empty($validationByWebsite[$websiteId])) {
3690+ continue;
3691+ }
3692+ foreach ($customerGroupIds as $customerGroupId) {
3693+ $rows[] = [
3694+ 'rule_id' => $ruleId,
3695+ 'from_time' => $fromTime,
3696+ 'to_time' => $toTime,
3697+ 'website_id' => $websiteId,
3698+ 'customer_group_id' => $customerGroupId,
3699+ 'product_id' => $productId,
3700+ 'action_operator' => $actionOperator,
3701+ 'action_amount' => $actionAmount,
3702+ 'action_stop' => $actionStop,
3703+ 'sort_order' => $sortOrder,
3704+ ];
3705+
3706+ if (count($rows) == $batchCount) {
3707+ $connection->insertMultiple($indexTable, $rows);
3708+ $rows = [];
3709+ }
3710+ }
3711+ }
3712+ }
3713+ if (!empty($rows)) {
3714+ $connection->insertMultiple($indexTable, $rows);
3715+ }
3716+ return true;
3717+ }
3718+}
3719diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProductPrice.php b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProductPrice.php
3720new file mode 100644
3721index 00000000000..6a87be3c50a
3722--- /dev/null
3723+++ b/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProductPrice.php
3724@@ -0,0 +1,168 @@
3725+<?php
3726+/**
3727+ * Copyright © Magento, Inc. All rights reserved.
3728+ * See COPYING.txt for license details.
3729+ */
3730+
3731+namespace Magento\CatalogRule\Model\Indexer;
3732+
3733+/**
3734+ * Reindex product prices according rule settings.
3735+ */
3736+class ReindexRuleProductPrice
3737+{
3738+ /**
3739+ * @var \Magento\Store\Model\StoreManagerInterface
3740+ */
3741+ private $storeManager;
3742+
3743+ /**
3744+ * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder
3745+ */
3746+ private $ruleProductsSelectBuilder;
3747+
3748+ /**
3749+ * @var \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator
3750+ */
3751+ private $productPriceCalculator;
3752+
3753+ /**
3754+ * @var \Magento\Framework\Stdlib\DateTime\DateTime
3755+ */
3756+ private $dateTime;
3757+
3758+ /**
3759+ * @var \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor
3760+ */
3761+ private $pricesPersistor;
3762+
3763+ /**
3764+ * @param \Magento\Store\Model\StoreManagerInterface $storeManager
3765+ * @param RuleProductsSelectBuilder $ruleProductsSelectBuilder
3766+ * @param ProductPriceCalculator $productPriceCalculator
3767+ * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
3768+ * @param \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor
3769+ */
3770+ public function __construct(
3771+ \Magento\Store\Model\StoreManagerInterface $storeManager,
3772+ \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder $ruleProductsSelectBuilder,
3773+ \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator $productPriceCalculator,
3774+ \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
3775+ \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor
3776+ ) {
3777+ $this->storeManager = $storeManager;
3778+ $this->ruleProductsSelectBuilder = $ruleProductsSelectBuilder;
3779+ $this->productPriceCalculator = $productPriceCalculator;
3780+ $this->dateTime = $dateTime;
3781+ $this->pricesPersistor = $pricesPersistor;
3782+ }
3783+
3784+ /**
3785+ * Reindex product prices.
3786+ *
3787+ * @param int $batchCount
3788+ * @param \Magento\Catalog\Model\Product|null $product
3789+ * @param bool $useAdditionalTable
3790+ * @return bool
3791+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
3792+ */
3793+ public function execute(
3794+ $batchCount,
3795+ \Magento\Catalog\Model\Product $product = null,
3796+ $useAdditionalTable = false
3797+ ) {
3798+ $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1);
3799+ $toDate = mktime(0, 0, 0, date('m'), date('d') + 1);
3800+
3801+ /**
3802+ * Update products rules prices per each website separately
3803+ * because of max join limit in mysql
3804+ */
3805+ foreach ($this->storeManager->getWebsites() as $website) {
3806+ $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $product, $useAdditionalTable);
3807+ $dayPrices = [];
3808+ $stopFlags = [];
3809+ $prevKey = null;
3810+
3811+ while ($ruleData = $productsStmt->fetch()) {
3812+ $ruleProductId = $ruleData['product_id'];
3813+ $productKey = $ruleProductId .
3814+ '_' .
3815+ $ruleData['website_id'] .
3816+ '_' .
3817+ $ruleData['customer_group_id'];
3818+
3819+ if ($prevKey && $prevKey != $productKey) {
3820+ $stopFlags = [];
3821+ if (count($dayPrices) > $batchCount) {
3822+ $this->pricesPersistor->execute($dayPrices, $useAdditionalTable);
3823+ $dayPrices = [];
3824+ }
3825+ }
3826+
3827+ $ruleData['from_time'] = $this->roundTime($ruleData['from_time']);
3828+ $ruleData['to_time'] = $this->roundTime($ruleData['to_time']);
3829+ /**
3830+ * Build prices for each day
3831+ */
3832+ for ($time = $fromDate; $time <= $toDate; $time += IndexBuilder::SECONDS_IN_DAY) {
3833+ if (($ruleData['from_time'] == 0 ||
3834+ $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 ||
3835+ $time <= $ruleData['to_time'])
3836+ ) {
3837+ $priceKey = $time . '_' . $productKey;
3838+
3839+ if (isset($stopFlags[$priceKey])) {
3840+ continue;
3841+ }
3842+
3843+ if (!isset($dayPrices[$priceKey])) {
3844+ $dayPrices[$priceKey] = [
3845+ 'rule_date' => $time,
3846+ 'website_id' => $ruleData['website_id'],
3847+ 'customer_group_id' => $ruleData['customer_group_id'],
3848+ 'product_id' => $ruleProductId,
3849+ 'rule_price' => $this->productPriceCalculator->calculate($ruleData),
3850+ 'latest_start_date' => $ruleData['from_time'],
3851+ 'earliest_end_date' => $ruleData['to_time'],
3852+ ];
3853+ } else {
3854+ $dayPrices[$priceKey]['rule_price'] = $this->productPriceCalculator->calculate(
3855+ $ruleData,
3856+ $dayPrices[$priceKey]
3857+ );
3858+ $dayPrices[$priceKey]['latest_start_date'] = max(
3859+ $dayPrices[$priceKey]['latest_start_date'],
3860+ $ruleData['from_time']
3861+ );
3862+ $dayPrices[$priceKey]['earliest_end_date'] = min(
3863+ $dayPrices[$priceKey]['earliest_end_date'],
3864+ $ruleData['to_time']
3865+ );
3866+ }
3867+
3868+ if ($ruleData['action_stop']) {
3869+ $stopFlags[$priceKey] = true;
3870+ }
3871+ }
3872+ }
3873+
3874+ $prevKey = $productKey;
3875+ }
3876+ $this->pricesPersistor->execute($dayPrices, $useAdditionalTable);
3877+ }
3878+ return true;
3879+ }
3880+
3881+ /**
3882+ * @param int $timeStamp
3883+ * @return int
3884+ */
3885+ private function roundTime($timeStamp)
3886+ {
3887+ if (is_numeric($timeStamp) && $timeStamp != 0) {
3888+ $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00', $timeStamp));
3889+ }
3890+ return $timeStamp;
3891+ }
3892+}
3893diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductPricesPersistor.php b/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductPricesPersistor.php
3894new file mode 100644
3895index 00000000000..ad5a2d25948
3896--- /dev/null
3897+++ b/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductPricesPersistor.php
3898@@ -0,0 +1,95 @@
3899+<?php
3900+/**
3901+ * Copyright © Magento, Inc. All rights reserved.
3902+ * See COPYING.txt for license details.
3903+ */
3904+
3905+namespace Magento\CatalogRule\Model\Indexer;
3906+
3907+use Magento\CatalogRule\Api\IndexerTableSwapperInterface as TableSwapper;
3908+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
3909+use Magento\Framework\App\ObjectManager;
3910+
3911+/**
3912+ * Persist product prices to index table.
3913+ */
3914+class RuleProductPricesPersistor
3915+{
3916+ /**
3917+ * @var \Magento\Framework\App\ResourceConnection
3918+ */
3919+ private $resource;
3920+
3921+ /**
3922+ * @var \Magento\Framework\Stdlib\DateTime
3923+ */
3924+ private $dateFormat;
3925+
3926+ /**
3927+ * @var TableSwapper
3928+ */
3929+ private $tableSwapper;
3930+
3931+ /**
3932+ * @param \Magento\Framework\Stdlib\DateTime $dateFormat
3933+ * @param \Magento\Framework\App\ResourceConnection $resource
3934+ * @param ActiveTableSwitcher $activeTableSwitcher
3935+ * @param TableSwapper|null $tableSwapper
3936+ *
3937+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
3938+ */
3939+ public function __construct(
3940+ \Magento\Framework\Stdlib\DateTime $dateFormat,
3941+ \Magento\Framework\App\ResourceConnection $resource,
3942+ ActiveTableSwitcher $activeTableSwitcher,
3943+ TableSwapper $tableSwapper = null
3944+ ) {
3945+ $this->dateFormat = $dateFormat;
3946+ $this->resource = $resource;
3947+ $this->tableSwapper = $tableSwapper ?: ObjectManager::getInstance()->get(TableSwapper::class);
3948+ }
3949+
3950+ /**
3951+ * Persist prices data to index table.
3952+ *
3953+ * @param array $priceData
3954+ * @param bool $useAdditionalTable
3955+ * @return bool
3956+ * @throws \Exception
3957+ */
3958+ public function execute(array $priceData, $useAdditionalTable = false)
3959+ {
3960+ if (empty($priceData)) {
3961+ return false;
3962+ }
3963+
3964+ $connection = $this->resource->getConnection();
3965+ $indexTable = $this->resource->getTableName('catalogrule_product_price');
3966+ if ($useAdditionalTable) {
3967+ $indexTable = $this->resource->getTableName(
3968+ $this->tableSwapper->getWorkingTableName('catalogrule_product_price')
3969+ );
3970+ }
3971+
3972+ $productIds = [];
3973+
3974+ try {
3975+ foreach ($priceData as $key => $data) {
3976+ $productIds['product_id'] = $data['product_id'];
3977+ $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false);
3978+ $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate(
3979+ $data['latest_start_date'],
3980+ false
3981+ );
3982+ $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate(
3983+ $data['earliest_end_date'],
3984+ false
3985+ );
3986+ }
3987+ $connection->insertOnDuplicate($indexTable, $priceData);
3988+ } catch (\Exception $e) {
3989+ throw $e;
3990+ }
3991+ return true;
3992+ }
3993+}
3994diff --git a/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductsSelectBuilder.php b/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductsSelectBuilder.php
3995new file mode 100644
3996index 00000000000..65ca07a524a
3997--- /dev/null
3998+++ b/vendor/magento/module-catalog-rule/Model/Indexer/RuleProductsSelectBuilder.php
3999@@ -0,0 +1,163 @@
4000+<?php
4001+/**
4002+ * Copyright © Magento, Inc. All rights reserved.
4003+ * See COPYING.txt for license details.
4004+ */
4005+
4006+namespace Magento\CatalogRule\Model\Indexer;
4007+
4008+use Magento\CatalogRule\Api\IndexerTableSwapperInterface as TableSwapper;
4009+use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
4010+use Magento\Framework\App\ObjectManager;
4011+
4012+/**
4013+ * Build select for rule relation with product.
4014+ */
4015+class RuleProductsSelectBuilder
4016+{
4017+ /**
4018+ * @var \Magento\Framework\App\ResourceConnection
4019+ */
4020+ private $resource;
4021+
4022+ /**
4023+ * @var \Magento\Eav\Model\Config
4024+ */
4025+ private $eavConfig;
4026+
4027+ /**
4028+ * @var \Magento\Store\Model\StoreManagerInterface
4029+ */
4030+ private $storeManager;
4031+
4032+ /**
4033+ * @var \Magento\Framework\EntityManager\MetadataPool
4034+ */
4035+ private $metadataPool;
4036+
4037+ /**
4038+ * @var TableSwapper
4039+ */
4040+ private $tableSwapper;
4041+
4042+ /**
4043+ * @param \Magento\Framework\App\ResourceConnection $resource
4044+ * @param \Magento\Eav\Model\Config $eavConfig
4045+ * @param \Magento\Store\Model\StoreManagerInterface $storeManager
4046+ * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
4047+ * @param ActiveTableSwitcher $activeTableSwitcher
4048+ * @param TableSwapper|null $tableSwapper
4049+ *
4050+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
4051+ */
4052+ public function __construct(
4053+ \Magento\Framework\App\ResourceConnection $resource,
4054+ \Magento\Eav\Model\Config $eavConfig,
4055+ \Magento\Store\Model\StoreManagerInterface $storeManager,
4056+ \Magento\Framework\EntityManager\MetadataPool $metadataPool,
4057+ ActiveTableSwitcher $activeTableSwitcher,
4058+ TableSwapper $tableSwapper = null
4059+ ) {
4060+ $this->eavConfig = $eavConfig;
4061+ $this->storeManager = $storeManager;
4062+ $this->metadataPool = $metadataPool;
4063+ $this->resource = $resource;
4064+ $this->tableSwapper = $tableSwapper ?: ObjectManager::getInstance()->get(TableSwapper::class);
4065+ }
4066+
4067+ /**
4068+ * Build select for indexer according passed parameters.
4069+ *
4070+ * @param int $websiteId
4071+ * @param \Magento\Catalog\Model\Product|null $product
4072+ * @param bool $useAdditionalTable
4073+ * @return \Zend_Db_Statement_Interface
4074+ */
4075+ public function build(
4076+ $websiteId,
4077+ \Magento\Catalog\Model\Product $product = null,
4078+ $useAdditionalTable = false
4079+ ) {
4080+ $connection = $this->resource->getConnection();
4081+ $indexTable = $this->resource->getTableName('catalogrule_product');
4082+ if ($useAdditionalTable) {
4083+ $indexTable = $this->resource->getTableName(
4084+ $this->tableSwapper->getWorkingTableName('catalogrule_product')
4085+ );
4086+ }
4087+
4088+ /**
4089+ * Sort order is important
4090+ * It used for check stop price rule condition.
4091+ * website_id customer_group_id product_id sort_order
4092+ * 1 1 1 0
4093+ * 1 1 1 1
4094+ * 1 1 1 2
4095+ * if row with sort order 1 will have stop flag we should exclude
4096+ * all next rows for same product id from price calculation
4097+ */
4098+ $select = $connection->select()->from(
4099+ ['rp' => $indexTable]
4100+ )->order(
4101+ ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id']
4102+ );
4103+
4104+ if ($product && $product->getEntityId()) {
4105+ $select->where('rp.product_id=?', $product->getEntityId());
4106+ }
4107+
4108+ /**
4109+ * Join default price and websites prices to result
4110+ */
4111+ $priceAttr = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'price');
4112+ $priceTable = $priceAttr->getBackend()->getTable();
4113+ $attributeId = $priceAttr->getId();
4114+
4115+ $linkField = $this->metadataPool
4116+ ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
4117+ ->getLinkField();
4118+ $select->join(
4119+ ['e' => $this->resource->getTableName('catalog_product_entity')],
4120+ sprintf('e.entity_id = rp.product_id'),
4121+ []
4122+ );
4123+ $joinCondition = '%1$s.' . $linkField . '=e.' . $linkField . ' AND (%1$s.attribute_id='
4124+ . $attributeId
4125+ . ') and %1$s.store_id=%2$s';
4126+
4127+ $select->join(
4128+ ['pp_default' => $priceTable],
4129+ sprintf($joinCondition, 'pp_default', \Magento\Store\Model\Store::DEFAULT_STORE_ID),
4130+ []
4131+ );
4132+
4133+ $website = $this->storeManager->getWebsite($websiteId);
4134+ $defaultGroup = $website->getDefaultGroup();
4135+ if ($defaultGroup instanceof \Magento\Store\Model\Group) {
4136+ $storeId = $defaultGroup->getDefaultStoreId();
4137+ } else {
4138+ $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID;
4139+ }
4140+
4141+ $select->joinInner(
4142+ ['product_website' => $this->resource->getTableName('catalog_product_website')],
4143+ 'product_website.product_id=rp.product_id '
4144+ . 'AND product_website.website_id = rp.website_id '
4145+ . 'AND product_website.website_id='
4146+ . $websiteId,
4147+ []
4148+ );
4149+
4150+ $tableAlias = 'pp' . $websiteId;
4151+ $select->joinLeft(
4152+ [$tableAlias => $priceTable],
4153+ sprintf($joinCondition, $tableAlias, $storeId),
4154+ []
4155+ );
4156+ $select->columns([
4157+ 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'),
4158+ ]);
4159+
4160+ return $connection->query($select);
4161+ }
4162+}
4163diff --git a/vendor/magento/module-catalog-rule/Model/Rule.php b/vendor/magento/module-catalog-rule/Model/Rule.php
4164index f94bdb5fa80..ef1c286eb9b 100644
4165--- a/vendor/magento/module-catalog-rule/Model/Rule.php
4166+++ b/vendor/magento/module-catalog-rule/Model/Rule.php
4167@@ -505,9 +505,11 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog
4168 public function afterSave()
4169 {
4170 if ($this->isObjectNew()) {
4171- $this->getMatchingProductIds();
4172- if (!empty($this->_productIds) && is_array($this->_productIds)) {
4173- $this->_ruleProductProcessor->reindexList($this->_productIds);
4174+ if (!$this->_ruleProductProcessor->isIndexerScheduled()) {
4175+ $this->getMatchingProductIds();
4176+ if (!empty($this->_productIds) && is_array($this->_productIds)) {
4177+ $this->_getResource()->addCommitCallback([$this, 'reindex']);
4178+ }
4179 }
4180 } else {
4181 $this->_ruleProductProcessor->getIndexer()->invalidate();
4182@@ -515,6 +517,16 @@ class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\Catalog
4183 return parent::afterSave();
4184 }
4185
4186+ /**
4187+ * Init indexing process after rule save
4188+ *
4189+ * @return void
4190+ */
4191+ public function reindex()
4192+ {
4193+ $this->_ruleProductProcessor->reindexList($this->_productIds);
4194+ }
4195+
4196 /**
4197 * {@inheritdoc}
4198 *
4199diff --git a/vendor/magento/module-catalog-rule/Setup/UpgradeSchema.php b/vendor/magento/module-catalog-rule/Setup/UpgradeSchema.php
4200index ac667186a9a..c86f02c40a5 100644
4201--- a/vendor/magento/module-catalog-rule/Setup/UpgradeSchema.php
4202+++ b/vendor/magento/module-catalog-rule/Setup/UpgradeSchema.php
4203@@ -26,6 +26,31 @@ class UpgradeSchema implements UpgradeSchemaInterface
4204 $this->removeSubProductDiscounts($setup);
4205 }
4206
4207+ if (version_compare($context->getVersion(), '2.0.1.1.2', '<')) {
4208+ $connection = $setup->getConnection();
4209+ $connection->dropForeignKey(
4210+ $setup->getTable('catalogrule_group_website'),
4211+ $setup->getFkName(
4212+ 'catalogrule_group_website',
4213+ 'customer_group_id',
4214+ 'customer_group',
4215+ 'customer_group_id'
4216+ )
4217+ );
4218+ $connection->dropForeignKey(
4219+ $setup->getTable('catalogrule_group_website'),
4220+ $setup->getFkName('catalogrule_group_website', 'rule_id', 'catalogrule', 'rule_id')
4221+ );
4222+ $connection->dropForeignKey(
4223+ $setup->getTable('catalogrule_group_website'),
4224+ $setup->getFkName('catalogrule_group_website', 'website_id', 'store_website', 'website_id')
4225+ );
4226+
4227+ $this->addReplicaTable($setup, 'catalogrule_product', 'catalogrule_product_replica');
4228+ $this->addReplicaTable($setup, 'catalogrule_product_price', 'catalogrule_product_price_replica');
4229+ $this->addReplicaTable($setup, 'catalogrule_group_website', 'catalogrule_group_website_replica');
4230+ }
4231+
4232 $setup->endSetup();
4233 }
4234
4235@@ -55,4 +80,22 @@ class UpgradeSchema implements UpgradeSchemaInterface
4236 }
4237 }
4238 }
4239+
4240+ /**
4241+ * Add the replica table for existing one.
4242+ *
4243+ * @param SchemaSetupInterface $setup
4244+ * @param string $existingTable
4245+ * @param string $replicaTable
4246+ * @return void
4247+ */
4248+ private function addReplicaTable(SchemaSetupInterface $setup, $existingTable, $replicaTable)
4249+ {
4250+ $sql = sprintf(
4251+ 'CREATE TABLE IF NOT EXISTS %s LIKE %s',
4252+ $setup->getTable($replicaTable),
4253+ $setup->getTable($existingTable)
4254+ );
4255+ $setup->getConnection()->query($sql);
4256+ }
4257 }
4258diff --git a/vendor/magento/module-catalog-rule/etc/di.xml b/vendor/magento/module-catalog-rule/etc/di.xml
4259index 6783fd6d69b..1aaed9c3cbd 100644
4260--- a/vendor/magento/module-catalog-rule/etc/di.xml
4261+++ b/vendor/magento/module-catalog-rule/etc/di.xml
4262@@ -126,4 +126,5 @@
4263 </argument>
4264 </arguments>
4265 </type>
4266+ <preference for="Magento\CatalogRule\Api\IndexerTableSwapperInterface" type="Magento\CatalogRule\Model\Indexer\IndexerTableSwapper" />
4267 </config>
4268diff --git a/vendor/magento/module-catalog-rule/etc/module.xml b/vendor/magento/module-catalog-rule/etc/module.xml
4269index 6a864895ffe..d0af39d3f01 100644
4270--- a/vendor/magento/module-catalog-rule/etc/module.xml
4271+++ b/vendor/magento/module-catalog-rule/etc/module.xml
4272@@ -6,7 +6,7 @@
4273 */
4274 -->
4275 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
4276- <module name="Magento_CatalogRule" setup_version="2.0.1">
4277+ <module name="Magento_CatalogRule" setup_version="2.0.1.1.2">
4278 <sequence>
4279 <module name="Magento_Rule"/>
4280 <module name="Magento_Catalog"/>
4281diff --git a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Aggregation/DataProvider.php b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Aggregation/DataProvider.php
4282index 495b22048b4..0c8c26904d1 100644
4283--- a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Aggregation/DataProvider.php
4284+++ b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Aggregation/DataProvider.php
4285@@ -6,6 +6,7 @@
4286 namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation;
4287
4288 use Magento\Catalog\Model\Product;
4289+use Magento\CatalogInventory\Model\Stock;
4290 use Magento\Customer\Model\Session;
4291 use Magento\Eav\Model\Config;
4292 use Magento\Framework\App\ResourceConnection;
4293@@ -15,7 +16,6 @@ use Magento\Framework\DB\Ddl\Table;
4294 use Magento\Framework\DB\Select;
4295 use Magento\Framework\Search\Adapter\Mysql\Aggregation\DataProviderInterface;
4296 use Magento\Framework\Search\Request\BucketInterface;
4297-use Magento\CatalogInventory\Model\Stock;
4298
4299 /**
4300 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
4301diff --git a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/AliasResolver.php b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/AliasResolver.php
4302index 52a2c6ff40c..8f5a231b1dd 100644
4303--- a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/AliasResolver.php
4304+++ b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/AliasResolver.php
4305@@ -6,24 +6,22 @@
4306
4307 namespace Magento\CatalogSearch\Model\Adapter\Mysql\Filter;
4308
4309+
4310 use Magento\CatalogSearch\Model\Search\RequestGenerator;
4311
4312 /**
4313- * Purpose of class is to resolve table alias for Search Request filter.
4314+ * Purpose of class is to resolve table alias for Search Request filter
4315 */
4316 class AliasResolver
4317 {
4318 /**
4319- * The suffix for stock status filter that may be added to the query beside the filter query.
4320+ * The suffix for stock status filter that may be added to the query beside the filter query
4321 * Used when showing of Out of Stock products is disabled.
4322 */
4323 const STOCK_FILTER_SUFFIX = '_stock';
4324
4325 /**
4326- * Returns alias of the filter in database.
4327- *
4328 * @param \Magento\Framework\Search\Request\FilterInterface $filter
4329- *
4330 * @return string alias of the filter in database
4331 */
4332 public function getAlias(\Magento\Framework\Search\Request\FilterInterface $filter)
4333@@ -41,7 +39,6 @@ class AliasResolver
4334 $alias = $field . RequestGenerator::FILTER_SUFFIX;
4335 break;
4336 }
4337-
4338 return $alias;
4339 }
4340 }
4341diff --git a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/Preprocessor.php b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/Preprocessor.php
4342index 05652445264..27a80aba56a 100644
4343--- a/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/Preprocessor.php
4344+++ b/vendor/magento/module-catalog-search/Model/Adapter/Mysql/Filter/Preprocessor.php
4345@@ -8,8 +8,11 @@ namespace Magento\CatalogSearch\Model\Adapter\Mysql\Filter;
4346 use Magento\Catalog\Api\Data\ProductInterface;
4347 use Magento\Catalog\Model\Product;
4348 use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
4349+use Magento\CatalogInventory\Model\Stock;
4350 use Magento\CatalogSearch\Model\Search\TableMapper;
4351 use Magento\Eav\Model\Config;
4352+use Magento\Framework\App\Config\ScopeConfigInterface;
4353+use Magento\Framework\App\ObjectManager;
4354 use Magento\Framework\App\ResourceConnection;
4355 use Magento\Framework\App\ScopeResolverInterface;
4356 use Magento\Framework\DB\Adapter\AdapterInterface;
4357@@ -17,11 +20,8 @@ use Magento\Framework\EntityManager\MetadataPool;
4358 use Magento\Framework\Search\Adapter\Mysql\ConditionManager;
4359 use Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface;
4360 use Magento\Framework\Search\Request\FilterInterface;
4361-use Magento\Store\Model\Store;
4362-use Magento\CatalogInventory\Model\Stock;
4363-use Magento\Framework\App\Config\ScopeConfigInterface;
4364-use Magento\Framework\App\ObjectManager;
4365 use Magento\Store\Model\ScopeInterface;
4366+use Magento\Store\Model\Store;
4367
4368 /**
4369 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
4370@@ -64,22 +64,11 @@ class Preprocessor implements PreprocessorInterface
4371 private $metadataPool;
4372
4373 /**
4374- * @deprecated
4375- *
4376- * @var TableMapper
4377- */
4378- private $tableMapper;
4379-
4380- /**
4381- * Scope config.
4382- *
4383 * @var ScopeConfigInterface
4384 */
4385 private $scopeConfig;
4386
4387 /**
4388- * Resolving table alias for Search Request filter.
4389- *
4390 * @var AliasResolver
4391 */
4392 private $aliasResolver;
4393@@ -91,8 +80,9 @@ class Preprocessor implements PreprocessorInterface
4394 * @param ResourceConnection $resource
4395 * @param TableMapper $tableMapper
4396 * @param string $attributePrefix
4397- * @param ScopeConfigInterface $scopeConfig
4398- * @param AliasResolver $aliasResolver
4399+ * @param ScopeConfigInterface|null $scopeConfig
4400+ * @param AliasResolver|null $aliasResolver
4401+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
4402 */
4403 public function __construct(
4404 ConditionManager $conditionManager,
4405@@ -110,7 +100,6 @@ class Preprocessor implements PreprocessorInterface
4406 $this->resource = $resource;
4407 $this->connection = $resource->getConnection();
4408 $this->attributePrefix = $attributePrefix;
4409- $this->tableMapper = $tableMapper;
4410 $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
4411 $this->aliasResolver = $aliasResolver ?: ObjectManager::getInstance()->get(AliasResolver::class);
4412 }
4413@@ -264,8 +253,6 @@ class Preprocessor implements PreprocessorInterface
4414 }
4415
4416 /**
4417- * Checks if it is necessary to show out of stock products.
4418- *
4419 * @return bool
4420 */
4421 private function isAddStockFilter()
4422@@ -274,7 +261,6 @@ class Preprocessor implements PreprocessorInterface
4423 'cataloginventory/options/show_out_of_stock',
4424 ScopeInterface::SCOPE_STORE
4425 );
4426-
4427 return false === $isShowOutOfStock;
4428 }
4429
4430diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
4431index 0783bdb424e..fff770bc446 100644
4432--- a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
4433+++ b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php
4434@@ -6,11 +6,16 @@
4435 namespace Magento\CatalogSearch\Model\Indexer;
4436
4437 use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\FullFactory;
4438+use Magento\CatalogSearch\Model\Indexer\Scope\State;
4439 use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
4440-use \Magento\Framework\Search\Request\Config as SearchRequestConfig;
4441+use Magento\Framework\App\ObjectManager;
4442+use Magento\Framework\Search\Request\Config as SearchRequestConfig;
4443 use Magento\Framework\Search\Request\DimensionFactory;
4444 use Magento\Store\Model\StoreManagerInterface;
4445
4446+/**
4447+ * Provide functionality for Fulltext Search indexing
4448+ */
4449 class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface
4450 {
4451 /**
4452@@ -18,34 +23,51 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
4453 */
4454 const INDEXER_ID = 'catalogsearch_fulltext';
4455
4456- /** @var array index structure */
4457+ /**
4458+ * @var array index structure
4459+ */
4460 protected $data;
4461
4462 /**
4463 * @var IndexerHandlerFactory
4464 */
4465 private $indexerHandlerFactory;
4466+
4467 /**
4468 * @var StoreManagerInterface
4469 */
4470 private $storeManager;
4471+
4472 /**
4473- * @var DimensionFactory
4474+ * @var \Magento\Framework\Search\Request\DimensionFactory
4475 */
4476 private $dimensionFactory;
4477+
4478 /**
4479- * @var Full
4480+ * @var \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full
4481 */
4482 private $fullAction;
4483+
4484 /**
4485 * @var FulltextResource
4486 */
4487 private $fulltextResource;
4488+
4489 /**
4490- * @var SearchRequestConfig
4491+ * @var \Magento\Framework\Search\Request\Config
4492 */
4493 private $searchRequestConfig;
4494
4495+ /**
4496+ * @var IndexSwitcherInterface
4497+ */
4498+ private $indexSwitcher;
4499+
4500+ /**
4501+ * @var \Magento\CatalogSearch\Model\Indexer\Scope\State
4502+ */
4503+ private $indexScopeState;
4504+
4505 /**
4506 * @param FullFactory $fullActionFactory
4507 * @param IndexerHandlerFactory $indexerHandlerFactory
4508@@ -54,6 +76,8 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
4509 * @param FulltextResource $fulltextResource
4510 * @param SearchRequestConfig $searchRequestConfig
4511 * @param array $data
4512+ * @param IndexSwitcherInterface $indexSwitcher
4513+ * @param Scope\State $indexScopeState
4514 */
4515 public function __construct(
4516 FullFactory $fullActionFactory,
4517@@ -62,7 +86,9 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
4518 DimensionFactory $dimensionFactory,
4519 FulltextResource $fulltextResource,
4520 SearchRequestConfig $searchRequestConfig,
4521- array $data
4522+ array $data,
4523+ IndexSwitcherInterface $indexSwitcher = null,
4524+ State $indexScopeState = null
4525 ) {
4526 $this->fullAction = $fullActionFactory->create(['data' => $data]);
4527 $this->indexerHandlerFactory = $indexerHandlerFactory;
4528@@ -71,6 +97,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
4529 $this->fulltextResource = $fulltextResource;
4530 $this->searchRequestConfig = $searchRequestConfig;
4531 $this->data = $data;
4532+ if (null === $indexSwitcher) {
4533+ $indexSwitcher = ObjectManager::getInstance()->get(IndexSwitcherInterface::class);
4534+ }
4535+ if (null === $indexScopeState) {
4536+ $indexScopeState = ObjectManager::getInstance()->get(State::class);
4537+ }
4538+ $this->indexSwitcher = $indexSwitcher;
4539+ $this->indexScopeState = $indexScopeState;
4540 }
4541
4542 /**
4543@@ -106,10 +140,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
4544 'data' => $this->data
4545 ]);
4546 foreach ($storeIds as $storeId) {
4547- $dimension = $this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId]);
4548- $saveHandler->cleanIndex([$dimension]);
4549- $saveHandler->saveIndex([$dimension], $this->fullAction->rebuildStoreIndex($storeId));
4550+ $dimensions = [$this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId])];
4551+ $this->indexScopeState->useTemporaryIndex();
4552+
4553+ $saveHandler->cleanIndex($dimensions);
4554+ $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId));
4555
4556+ $this->indexSwitcher->switchIndex($dimensions);
4557+ $this->indexScopeState->useRegularIndex();
4558 }
4559 $this->fulltextResource->resetSearchResults();
4560 $this->searchRequestConfig->reset();
4561diff --git a/vendor/magento/module-catalog-search/Model/Indexer/IndexStructure.php b/vendor/magento/module-catalog-search/Model/Indexer/IndexStructure.php
4562index 43c3eb670f4..92eff50cfc1 100644
4563--- a/vendor/magento/module-catalog-search/Model/Indexer/IndexStructure.php
4564+++ b/vendor/magento/module-catalog-search/Model/Indexer/IndexStructure.php
4565@@ -12,6 +12,7 @@ use Magento\Framework\DB\Ddl\Table;
4566 use Magento\Framework\Search\Request\Dimension;
4567 use Magento\Framework\Indexer\IndexStructureInterface;
4568 use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
4569+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
4570
4571 class IndexStructure implements IndexStructureInterface
4572 {
4573@@ -19,6 +20,7 @@ class IndexStructure implements IndexStructureInterface
4574 * @var Resource
4575 */
4576 private $resource;
4577+
4578 /**
4579 * @var IndexScopeResolver
4580 */
4581@@ -26,11 +28,11 @@ class IndexStructure implements IndexStructureInterface
4582
4583 /**
4584 * @param ResourceConnection $resource
4585- * @param IndexScopeResolver $indexScopeResolver
4586+ * @param IndexScopeResolverInterface $indexScopeResolver
4587 */
4588 public function __construct(
4589 ResourceConnection $resource,
4590- IndexScopeResolver $indexScopeResolver
4591+ IndexScopeResolverInterface $indexScopeResolver
4592 ) {
4593 $this->resource = $resource;
4594 $this->indexScopeResolver = $indexScopeResolver;
4595diff --git a/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherInterface.php b/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherInterface.php
4596new file mode 100644
4597index 00000000000..ba5fb461dde
4598--- /dev/null
4599+++ b/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherInterface.php
4600@@ -0,0 +1,22 @@
4601+<?php
4602+/**
4603+ * Copyright © 2016 Magento. All rights reserved.
4604+ * See COPYING.txt for license details.
4605+ */
4606+namespace Magento\CatalogSearch\Model\Indexer;
4607+
4608+/**
4609+ * Provides a functionality to replace main index with its temporary representation
4610+ */
4611+interface IndexSwitcherInterface
4612+{
4613+ /**
4614+ * Switch current index with temporary index
4615+ *
4616+ * It will drop current index table and rename temporary index table to the current index table.
4617+ *
4618+ * @param array $dimensions
4619+ * @return void
4620+ */
4621+ public function switchIndex(array $dimensions);
4622+}
4623diff --git a/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherProxy.php b/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherProxy.php
4624new file mode 100644
4625index 00000000000..2ce093ed99e
4626--- /dev/null
4627+++ b/vendor/magento/module-catalog-search/Model/Indexer/IndexSwitcherProxy.php
4628@@ -0,0 +1,100 @@
4629+<?php
4630+/**
4631+ * Copyright © 2016 Magento. All rights reserved.
4632+ * See COPYING.txt for license details.
4633+ */
4634+
4635+namespace Magento\CatalogSearch\Model\Indexer;
4636+
4637+use Magento\Framework\App\Config\ScopeConfigInterface;
4638+use Magento\Framework\ObjectManagerInterface;
4639+use Magento\Store\Model\ScopeInterface;
4640+
4641+/**
4642+ * Proxy for adapter-specific index switcher
4643+ */
4644+class IndexSwitcherProxy implements IndexSwitcherInterface
4645+{
4646+ /**
4647+ * Object Manager instance
4648+ *
4649+ * @var ObjectManagerInterface
4650+ */
4651+ private $objectManager = null;
4652+
4653+ /**
4654+ * Instance name to create
4655+ *
4656+ * @var string
4657+ */
4658+ private $handlers;
4659+
4660+ /**
4661+ * @var ScopeConfigInterface
4662+ */
4663+ private $scopeConfig;
4664+
4665+ /**
4666+ * Configuration path by which current indexer handler stored
4667+ *
4668+ * @var string
4669+ */
4670+ private $configPath;
4671+
4672+ /**
4673+ * Factory constructor
4674+ *
4675+ * @param ObjectManagerInterface $objectManager
4676+ * @param ScopeConfigInterface $scopeConfig
4677+ * @param string $configPath
4678+ * @param string[] $handlers
4679+ */
4680+ public function __construct(
4681+ ObjectManagerInterface $objectManager,
4682+ ScopeConfigInterface $scopeConfig,
4683+ $configPath,
4684+ array $handlers = []
4685+ ) {
4686+ $this->objectManager = $objectManager;
4687+ $this->scopeConfig = $scopeConfig;
4688+ $this->configPath = $configPath;
4689+ $this->handlers = $handlers;
4690+ }
4691+
4692+ /**
4693+ * {@inheritDoc}
4694+ *
4695+ * As index switcher is an optional part of the search SPI, it may be not defined by a search engine.
4696+ * It is especially reasonable for search engines with pre-defined indexes declaration (like old SOLR and Sphinx)
4697+ * which cannot create temporary indexes on the fly.
4698+ * That's the reason why this method do nothing for the case
4699+ * when switcher is not defined for a specific search engine.
4700+ */
4701+ public function switchIndex(array $dimensions)
4702+ {
4703+ $currentHandler = $this->scopeConfig->getValue($this->configPath, ScopeInterface::SCOPE_STORE);
4704+ if (!isset($this->handlers[$currentHandler])) {
4705+ return;
4706+ }
4707+ $this->create($currentHandler)->switchIndex($dimensions);
4708+ }
4709+
4710+ /**
4711+ * Create indexer handler
4712+ *
4713+ * @param string $handler
4714+ * @return IndexSwitcherInterface
4715+ */
4716+ private function create($handler)
4717+ {
4718+ $indexSwitcher = $this->objectManager->create($this->handlers[$handler]);
4719+
4720+ if (!$indexSwitcher instanceof IndexSwitcherInterface) {
4721+ throw new \InvalidArgumentException(
4722+ $handler . ' index switcher doesn\'t implement ' . IndexSwitcherInterface::class
4723+ );
4724+ }
4725+
4726+ return $indexSwitcher;
4727+ }
4728+}
4729diff --git a/vendor/magento/module-catalog-search/Model/Indexer/IndexerHandler.php b/vendor/magento/module-catalog-search/Model/Indexer/IndexerHandler.php
4730index 2d98f3c06b6..5051b76a41d 100644
4731--- a/vendor/magento/module-catalog-search/Model/Indexer/IndexerHandler.php
4732+++ b/vendor/magento/module-catalog-search/Model/Indexer/IndexerHandler.php
4733@@ -7,13 +7,11 @@ namespace Magento\CatalogSearch\Model\Indexer;
4734
4735 use Magento\Eav\Model\Config;
4736 use Magento\Framework\App\ResourceConnection;
4737-use Magento\Framework\DB\Adapter\AdapterInterface;
4738 use Magento\Framework\Indexer\SaveHandler\IndexerInterface;
4739 use Magento\Framework\Indexer\IndexStructureInterface;
4740 use Magento\Framework\Search\Request\Dimension;
4741 use Magento\Framework\Search\Request\IndexScopeResolverInterface;
4742 use Magento\Framework\Indexer\SaveHandler\Batch;
4743-use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
4744
4745 class IndexerHandler implements IndexerInterface
4746 {
4747@@ -62,7 +60,7 @@ class IndexerHandler implements IndexerInterface
4748 * @param ResourceConnection $resource
4749 * @param Config $eavConfig
4750 * @param Batch $batch
4751- * @param \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver $indexScopeResolver
4752+ * @param IndexScopeResolverInterface $indexScopeResolver
4753 * @param array $data
4754 * @param int $batchSize
4755 */
4756@@ -71,7 +69,7 @@ class IndexerHandler implements IndexerInterface
4757 ResourceConnection $resource,
4758 Config $eavConfig,
4759 Batch $batch,
4760- IndexScopeResolver $indexScopeResolver,
4761+ IndexScopeResolverInterface $indexScopeResolver,
4762 array $data,
4763 $batchSize = 100
4764 ) {
4765diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexSwitcher.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexSwitcher.php
4766new file mode 100644
4767index 00000000000..87a7b7110d3
4768--- /dev/null
4769+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexSwitcher.php
4770@@ -0,0 +1,76 @@
4771+<?php
4772+/**
4773+ * Copyright © 2016 Magento. All rights reserved.
4774+ * See COPYING.txt for license details.
4775+ */
4776+namespace Magento\CatalogSearch\Model\Indexer\Scope;
4777+
4778+use Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface;
4779+use Magento\Framework\App\ResourceConnection;
4780+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
4781+
4782+/**
4783+ * Provides a functionality to replace main index with its temporary representation
4784+ */
4785+class IndexSwitcher implements IndexSwitcherInterface
4786+{
4787+ /**
4788+ * @var Resource
4789+ */
4790+ private $resource;
4791+
4792+ /**
4793+ * @var ScopeProxy
4794+ */
4795+ private $resolver;
4796+
4797+ /**
4798+ * @var State
4799+ */
4800+ private $state;
4801+
4802+ /**
4803+ * @param ResourceConnection $resource
4804+ * @param IndexScopeResolverInterface $indexScopeResolver
4805+ * @param State $state
4806+ */
4807+ public function __construct(
4808+ ResourceConnection $resource,
4809+ IndexScopeResolverInterface $indexScopeResolver,
4810+ State $state
4811+ ) {
4812+ $this->resource = $resource;
4813+ $this->resolver = $indexScopeResolver;
4814+ $this->state = $state;
4815+ }
4816+
4817+ /**
4818+ * {@inheritdoc}
4819+ * @throws IndexTableNotExistException
4820+ */
4821+ public function switchIndex(array $dimensions)
4822+ {
4823+ if (State::USE_TEMPORARY_INDEX === $this->state->getState()) {
4824+ $index = \Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID;
4825+
4826+ $temporalIndexTable = $this->resolver->resolve($index, $dimensions);
4827+ if (!$this->resource->getConnection()->isTableExists($temporalIndexTable)) {
4828+ throw new IndexTableNotExistException(
4829+ __(
4830+ "Temporary table for index $index doesn't exist,"
4831+ . " which is inconsistent with state of scope resolver"
4832+ )
4833+ );
4834+ }
4835+
4836+ $this->state->useRegularIndex();
4837+ $tableName = $this->resolver->resolve($index, $dimensions);
4838+ if ($this->resource->getConnection()->isTableExists($tableName)) {
4839+ $this->resource->getConnection()->dropTable($tableName);
4840+ }
4841+
4842+ $this->resource->getConnection()->renameTable($temporalIndexTable, $tableName);
4843+ $this->state->useTemporaryIndex();
4844+ }
4845+ }
4846+}
4847diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexTableNotExistException.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexTableNotExistException.php
4848new file mode 100644
4849index 00000000000..6974f8c278a
4850--- /dev/null
4851+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/IndexTableNotExistException.php
4852@@ -0,0 +1,18 @@
4853+<?php
4854+/**
4855+ * Copyright © 2016 Magento. All rights reserved.
4856+ * See COPYING.txt for license details.
4857+ */
4858+
4859+namespace Magento\CatalogSearch\Model\Indexer\Scope;
4860+
4861+
4862+use Magento\Framework\Exception\LocalizedException;
4863+
4864+/**
4865+ * Exception which represents situation where temporary index table should be used somewhere,
4866+ * but it does not exist in a database
4867+ */
4868+class IndexTableNotExistException extends LocalizedException
4869+{
4870+}
4871diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/ScopeProxy.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/ScopeProxy.php
4872new file mode 100644
4873index 00000000000..14832af303b
4874--- /dev/null
4875+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/ScopeProxy.php
4876@@ -0,0 +1,76 @@
4877+<?php
4878+/**
4879+ * Copyright © 2016 Magento. All rights reserved.
4880+ * See COPYING.txt for license details.
4881+ */
4882+
4883+namespace Magento\CatalogSearch\Model\Indexer\Scope;
4884+
4885+
4886+use Magento\Framework\Search\Request\Dimension;
4887+
4888+/**
4889+ * Implementation of IndexScopeResolverInterface which resolves index scope dynamically
4890+ * depending on current scope state
4891+ */
4892+class ScopeProxy implements \Magento\Framework\Search\Request\IndexScopeResolverInterface
4893+{
4894+ /**
4895+ * Object Manager instance
4896+ *
4897+ * @var \Magento\Framework\ObjectManagerInterface
4898+ */
4899+ private $objectManager;
4900+
4901+ /**
4902+ * @var array
4903+ */
4904+ private $states = [];
4905+
4906+ /**
4907+ * @var State
4908+ */
4909+ private $scopeState;
4910+
4911+ /**
4912+ * Factory constructor
4913+ *
4914+ * @param \Magento\Framework\ObjectManagerInterface $objectManager
4915+ * @param State $scopeState
4916+ * @param array $states
4917+ */
4918+ public function __construct(
4919+ \Magento\Framework\ObjectManagerInterface $objectManager,
4920+ State $scopeState,
4921+ array $states
4922+ ) {
4923+ $this->objectManager = $objectManager;
4924+ $this->scopeState = $scopeState;
4925+ $this->states = $states;
4926+ }
4927+
4928+ /**
4929+ * Creates class instance with specified parameters
4930+ *
4931+ * @param string $state
4932+ * @return \Magento\Framework\Search\Request\IndexScopeResolverInterface
4933+ * @throws UnknownStateException
4934+ */
4935+ private function create($state)
4936+ {
4937+ if (!array_key_exists($state, $this->states)) {
4938+ throw new UnknownStateException(__("Requested resolver for unknown indexer state: $state"));
4939+ }
4940+ return $this->objectManager->create($this->states[$state]);
4941+ }
4942+
4943+ /**
4944+ * @param string $index
4945+ * @param Dimension[] $dimensions
4946+ * @return string
4947+ */
4948+ public function resolve($index, array $dimensions)
4949+ {
4950+ return $this->create($this->scopeState->getState())->resolve($index, $dimensions);
4951+ }
4952+}
4953diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/State.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/State.php
4954new file mode 100644
4955index 00000000000..2bba29ae8d8
4956--- /dev/null
4957+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/State.php
4958@@ -0,0 +1,65 @@
4959+<?php
4960+/**
4961+ * Copyright © 2016 Magento. All rights reserved.
4962+ * See COPYING.txt for license details.
4963+ */
4964+
4965+namespace Magento\CatalogSearch\Model\Indexer\Scope;
4966+
4967+
4968+use Magento\Framework\App\ResourceConnection;
4969+use Magento\Framework\App\ScopeResolverInterface;
4970+use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
4971+use Magento\Framework\Search\Request\Dimension;
4972+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
4973+
4974+/**
4975+ * This class represents state that defines which table should be used during indexation process
4976+ *
4977+ * There are two possible states:
4978+ * - use_temporary_table
4979+ * - use_main_table
4980+ *
4981+ * The 'use_main_table' state means that default indexer table should be used.
4982+ *
4983+ * The 'use_temporary_table' state is an opposite for 'use_main_table'
4984+ * which means that default indexer table should be left unchanged during indexation
4985+ * and temporary table should be used instead.
4986+ *
4987+ */
4988+class State
4989+{
4990+ const USE_TEMPORARY_INDEX = 'use_temporary_table';
4991+ const USE_REGULAR_INDEX = 'use_main_table';
4992+
4993+ /**
4994+ * @var string
4995+ */
4996+ private $state = self::USE_REGULAR_INDEX;
4997+
4998+ /**
4999+ * Set the state to use temporary Index
5000+ * @return void
5001+ */
5002+ public function useTemporaryIndex()
5003+ {
5004+ $this->state = self::USE_TEMPORARY_INDEX;
5005+ }
5006+
5007+ /**
5008+ * Set the state to use regular Index
5009+ * @return void
5010+ */
5011+ public function useRegularIndex()
5012+ {
5013+ $this->state = self::USE_REGULAR_INDEX;
5014+ }
5015+
5016+ /**
5017+ * @return string
5018+ */
5019+ public function getState()
5020+ {
5021+ return $this->state;
5022+ }
5023+}
5024diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/TemporaryResolver.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/TemporaryResolver.php
5025new file mode 100644
5026index 00000000000..51037eb637c
5027--- /dev/null
5028+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/TemporaryResolver.php
5029@@ -0,0 +1,43 @@
5030+<?php
5031+/**
5032+ * Copyright © 2016 Magento. All rights reserved.
5033+ * See COPYING.txt for license details.
5034+ */
5035+
5036+namespace Magento\CatalogSearch\Model\Indexer\Scope;
5037+
5038+
5039+use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
5040+use Magento\Framework\Search\Request\Dimension;
5041+
5042+/**
5043+ * Resolves name of a temporary table for indexation
5044+ */
5045+class TemporaryResolver implements \Magento\Framework\Search\Request\IndexScopeResolverInterface
5046+{
5047+ /**
5048+ * @var IndexScopeResolver
5049+ */
5050+ private $indexScopeResolver;
5051+
5052+ /**
5053+ * @inheritDoc
5054+ */
5055+ public function __construct(IndexScopeResolver $indexScopeResolver)
5056+ {
5057+ $this->indexScopeResolver = $indexScopeResolver;
5058+ }
5059+
5060+ /**
5061+ * @param string $index
5062+ * @param Dimension[] $dimensions
5063+ * @return string
5064+ */
5065+ public function resolve($index, array $dimensions)
5066+ {
5067+ $tableName = $this->indexScopeResolver->resolve($index, $dimensions);
5068+ $tableName .= \Magento\Framework\Indexer\Table\StrategyInterface::TMP_SUFFIX;
5069+
5070+ return $tableName;
5071+ }
5072+}
5073diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Scope/UnknownStateException.php b/vendor/magento/module-catalog-search/Model/Indexer/Scope/UnknownStateException.php
5074new file mode 100644
5075index 00000000000..04803ef2748
5076--- /dev/null
5077+++ b/vendor/magento/module-catalog-search/Model/Indexer/Scope/UnknownStateException.php
5078@@ -0,0 +1,18 @@
5079+<?php
5080+/**
5081+ * Copyright © 2016 Magento. All rights reserved.
5082+ * See COPYING.txt for license details.
5083+ */
5084+
5085+namespace Magento\CatalogSearch\Model\Indexer\Scope;
5086+
5087+
5088+use Magento\Framework\Exception\LocalizedException;
5089+
5090+/**
5091+ * Exception for situation where used state which is not defined in configuration
5092+ */
5093+class UnknownStateException extends LocalizedException
5094+{
5095+
5096+}
5097diff --git a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/ExclusionStrategy.php b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/ExclusionStrategy.php
5098index 03675f27cac..9575f6efe51 100644
5099--- a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/ExclusionStrategy.php
5100+++ b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/ExclusionStrategy.php
5101@@ -9,27 +9,21 @@ namespace Magento\CatalogSearch\Model\Search\FilterMapper;
5102 use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
5103
5104 /**
5105- * Strategy which processes exclusions from general rules.
5106+ * Strategy which processes exclusions from general rules
5107 */
5108 class ExclusionStrategy implements FilterStrategyInterface
5109 {
5110 /**
5111- * Resource connection.
5112- *
5113 * @var \Magento\Framework\App\ResourceConnection
5114 */
5115- private $resourceConnection;
5116+ private $resourceConnection;
5117
5118 /**
5119- * Resolving table alias for Search Request filter.
5120- *
5121 * @var AliasResolver
5122 */
5123 private $aliasResolver;
5124
5125 /**
5126- * Store manager interface.
5127- *
5128 * @var \Magento\Store\Model\StoreManagerInterface
5129 */
5130 private $storeManager;
5131@@ -62,7 +56,9 @@ class ExclusionStrategy implements FilterStrategyInterface
5132 $alias = $this->aliasResolver->getAlias($filter);
5133 $tableName = $this->resourceConnection->getTableName('catalog_product_index_price');
5134 $select->joinInner(
5135- [$alias => $tableName],
5136+ [
5137+ $alias => $tableName
5138+ ],
5139 $this->resourceConnection->getConnection()->quoteInto(
5140 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ?',
5141 $this->storeManager->getWebsite()->getId()
5142@@ -74,13 +70,14 @@ class ExclusionStrategy implements FilterStrategyInterface
5143 $alias = $this->aliasResolver->getAlias($filter);
5144 $tableName = $this->resourceConnection->getTableName('catalog_category_product_index');
5145 $select->joinInner(
5146- [$alias => $tableName],
5147+ [
5148+ $alias => $tableName
5149+ ],
5150 'search_index.entity_id = category_ids_index.product_id',
5151 []
5152 );
5153 $isApplied = true;
5154 }
5155-
5156 return $isApplied;
5157 }
5158 }
5159diff --git a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterContext.php b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterContext.php
5160index e1901afce4f..d8d3efc65e7 100644
5161--- a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterContext.php
5162+++ b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterContext.php
5163@@ -6,47 +6,38 @@
5164
5165 namespace Magento\CatalogSearch\Model\Search\FilterMapper;
5166
5167+
5168 use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
5169 use Magento\Eav\Model\Config as EavConfig;
5170 use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
5171
5172 /**
5173- * FilterContext represents a Context of the Strategy pattern.
5174- * Its responsibility is to choose appropriate strategy to apply passed filter to the Select.
5175+ * FilterContext represents a Context of the Strategy pattern
5176+ * Its responsibility is to choose appropriate strategy to apply passed filter to the Select
5177 */
5178 class FilterContext implements FilterStrategyInterface
5179 {
5180 /**
5181- * Strategy which processes exclusions from general rules.
5182- *
5183 * @var ExclusionStrategy
5184 */
5185 private $exclusionStrategy;
5186
5187 /**
5188- * Eav attributes config.
5189- *
5190 * @var EavConfig
5191 */
5192 private $eavConfig;
5193
5194 /**
5195- * Strategy for handling dropdown or multi-select attributes.
5196- *
5197 * @var TermDropdownStrategy
5198 */
5199 private $termDropdownStrategy;
5200
5201 /**
5202- * Strategy for handling static attributes.
5203- *
5204 * @var StaticAttributeStrategy
5205 */
5206 private $staticAttributeStrategy;
5207
5208 /**
5209- * Resolving table alias for Search Request filter.
5210- *
5211 * @var AliasResolver
5212 */
5213 private $aliasResolver;
5214@@ -83,7 +74,6 @@ class FilterContext implements FilterStrategyInterface
5215
5216 if (!$isApplied) {
5217 $attribute = $this->getAttributeByCode($filter->getField());
5218-
5219 if ($attribute) {
5220 if ($filter->getType() === \Magento\Framework\Search\Request\FilterInterface::TYPE_TERM
5221 && in_array($attribute->getFrontendInput(), ['select', 'multiselect'], true)
5222@@ -99,12 +89,8 @@ class FilterContext implements FilterStrategyInterface
5223 }
5224
5225 /**
5226- * Returns attribute by attribute_code.
5227- *
5228 * @param string $field
5229- *
5230 * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
5231- *
5232 * @throws \Magento\Framework\Exception\LocalizedException
5233 */
5234 private function getAttributeByCode($field)
5235diff --git a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterStrategyInterface.php b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterStrategyInterface.php
5236index 0e41e0cd69f..92b06154977 100644
5237--- a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterStrategyInterface.php
5238+++ b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/FilterStrategyInterface.php
5239@@ -7,16 +7,13 @@
5240 namespace Magento\CatalogSearch\Model\Search\FilterMapper;
5241
5242 /**
5243- * FilterStrategyInterface provides the interface to work with strategies.
5244+ * FilterStrategyInterface provides the interface to work with strategies
5245 */
5246 interface FilterStrategyInterface
5247 {
5248 /**
5249- * Applies filter.
5250- *
5251 * @param \Magento\Framework\Search\Request\FilterInterface $filter
5252 * @param \Magento\Framework\DB\Select $select
5253- *
5254 * @return bool is filter was applied
5255 */
5256 public function apply(
5257diff --git a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/StaticAttributeStrategy.php b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/StaticAttributeStrategy.php
5258index a930219d2e2..bb4a18ce163 100644
5259--- a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/StaticAttributeStrategy.php
5260+++ b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/StaticAttributeStrategy.php
5261@@ -10,27 +10,21 @@ use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
5262 use Magento\Eav\Model\Config as EavConfig;
5263
5264 /**
5265- * This strategy handles static attributes.
5266+ * This strategy handles static attributes
5267 */
5268 class StaticAttributeStrategy implements FilterStrategyInterface
5269 {
5270 /**
5271- * Resource connection.
5272- *
5273 * @var \Magento\Framework\App\ResourceConnection
5274 */
5275 private $resourceConnection;
5276
5277 /**
5278- * Resolving table alias for Search Request filter.
5279- *
5280 * @var AliasResolver
5281 */
5282 private $aliasResolver;
5283
5284 /**
5285- * Eav attributes config.
5286- *
5287 * @var EavConfig
5288 */
5289 private $eavConfig;
5290@@ -65,17 +59,12 @@ class StaticAttributeStrategy implements FilterStrategyInterface
5291 . $this->resourceConnection->getConnection()->quoteIdentifier("$alias.entity_id"),
5292 []
5293 );
5294-
5295 return true;
5296 }
5297
5298 /**
5299- * Returns attribute by attribute_code.
5300- *
5301 * @param string $field
5302- *
5303 * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
5304- *
5305 * @throws \Magento\Framework\Exception\LocalizedException
5306 */
5307 private function getAttributeByCode($field)
5308diff --git a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/TermDropdownStrategy.php b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/TermDropdownStrategy.php
5309index a0f7f706b41..917450044a6 100644
5310--- a/vendor/magento/module-catalog-search/Model/Search/FilterMapper/TermDropdownStrategy.php
5311+++ b/vendor/magento/module-catalog-search/Model/Search/FilterMapper/TermDropdownStrategy.php
5312@@ -23,36 +23,26 @@ use Magento\Store\Model\StoreManagerInterface;
5313 class TermDropdownStrategy implements FilterStrategyInterface
5314 {
5315 /**
5316- * Resolving table alias for Search Request filter.
5317- *
5318 * @var AliasResolver
5319 */
5320 private $aliasResolver;
5321
5322 /**
5323- * Store manager.
5324- *
5325 * @var StoreManagerInterface
5326 */
5327 private $storeManager;
5328
5329 /**
5330- * Eav attributes config.
5331- *
5332 * @var EavConfig
5333 */
5334 private $eavConfig;
5335
5336 /**
5337- * Resource connection.
5338- *
5339 * @var ResourceConnection
5340 */
5341 private $resourceConnection;
5342
5343 /**
5344- * Scope config.
5345- *
5346 * @var ScopeConfigInterface
5347 */
5348 private $scopeConfig;
5349@@ -63,6 +53,7 @@ class TermDropdownStrategy implements FilterStrategyInterface
5350 * @param EavConfig $eavConfig
5351 * @param ScopeConfigInterface $scopeConfig
5352 * @param AliasResolver $aliasResolver
5353+ * @SuppressWarnings(Magento.TypeDuplication)
5354 */
5355 public function __construct(
5356 StoreManagerInterface $storeManager,
5357@@ -79,13 +70,7 @@ class TermDropdownStrategy implements FilterStrategyInterface
5358 }
5359
5360 /**
5361- * Applies filter.
5362- *
5363- * @param \Magento\Framework\Search\Request\FilterInterface $filter
5364- * @param \Magento\Framework\DB\Select $select
5365- *
5366- * @return bool is filter was applied
5367- *
5368+ * {@inheritDoc}
5369 * @throws \Magento\Framework\Exception\LocalizedException
5370 */
5371 public function apply(
5372@@ -105,11 +90,12 @@ class TermDropdownStrategy implements FilterStrategyInterface
5373 $joinCondition,
5374 []
5375 );
5376-
5377 if ($this->isAddStockFilter()) {
5378 $stockAlias = $alias . AliasResolver::STOCK_FILTER_SUFFIX;
5379 $select->joinLeft(
5380- [$stockAlias => $this->resourceConnection->getTableName('cataloginventory_stock_status')],
5381+ [
5382+ $stockAlias => $this->resourceConnection->getTableName('cataloginventory_stock_status'),
5383+ ],
5384 sprintf('%2$s.product_id = %1$s.source_id', $alias, $stockAlias),
5385 []
5386 );
5387@@ -119,12 +105,8 @@ class TermDropdownStrategy implements FilterStrategyInterface
5388 }
5389
5390 /**
5391- * Returns attribute by attribute code.
5392- *
5393 * @param string $field
5394- *
5395 * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
5396- *
5397 * @throws \Magento\Framework\Exception\LocalizedException
5398 */
5399 private function getAttributeByCode($field)
5400@@ -133,8 +115,6 @@ class TermDropdownStrategy implements FilterStrategyInterface
5401 }
5402
5403 /**
5404- * Check if it is necessary to show out of stock products.
5405- *
5406 * @return bool
5407 */
5408 private function isAddStockFilter()
5409diff --git a/vendor/magento/module-catalog-search/Model/Search/IndexBuilder.php b/vendor/magento/module-catalog-search/Model/Search/IndexBuilder.php
5410index 03129b0d698..05995895ed3 100644
5411--- a/vendor/magento/module-catalog-search/Model/Search/IndexBuilder.php
5412+++ b/vendor/magento/module-catalog-search/Model/Search/IndexBuilder.php
5413@@ -21,7 +21,7 @@ use Magento\CatalogInventory\Model\Stock;
5414 use Magento\Framework\App\ScopeResolverInterface;
5415
5416 /**
5417- * Build base Query for Index.
5418+ * Build base Query for Index
5419 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
5420 */
5421 class IndexBuilder implements IndexBuilderInterface
5422@@ -148,7 +148,7 @@ class IndexBuilder implements IndexBuilderInterface
5423 {
5424 if ($this->stockConfiguration === null) {
5425 $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance()
5426- ->get(\Magento\CatalogInventory\Api\StockConfigurationInterface::class);
5427+ ->get('Magento\CatalogInventory\Api\StockConfigurationInterface');
5428 }
5429 return $this->stockConfiguration;
5430 }
5431diff --git a/vendor/magento/module-catalog-search/Model/Search/TableMapper.php b/vendor/magento/module-catalog-search/Model/Search/TableMapper.php
5432index 2b8b79127c9..e96c6a36e20 100644
5433--- a/vendor/magento/module-catalog-search/Model/Search/TableMapper.php
5434+++ b/vendor/magento/module-catalog-search/Model/Search/TableMapper.php
5435@@ -24,7 +24,7 @@ use Magento\Framework\App\ObjectManager;
5436 /**
5437 * Responsibility of the TableMapper is to collect all filters from the search query
5438 * and pass them one by one for processing in the FilterContext,
5439- * which will apply them to the Select.
5440+ * which will apply them to the Select
5441 *
5442 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
5443 * @SuppressWarnings(PHPMD.NPathComplexity)
5444@@ -32,8 +32,6 @@ use Magento\Framework\App\ObjectManager;
5445 class TableMapper
5446 {
5447 /**
5448- * Resource connection.
5449- *
5450 * @var AppResource
5451 */
5452 private $resource;
5453@@ -44,49 +42,36 @@ class TableMapper
5454 private $storeManager;
5455
5456 /**
5457- * @deprecated
5458- *
5459- * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
5460- */
5461- private $attributeCollection;
5462-
5463- /**
5464- * Eav attribute config.
5465- *
5466 * @var EavConfig
5467 */
5468- private $eavConfig;
5469+ private $eavConfig;
5470
5471 /**
5472- * Scope config.
5473- *
5474 * @var ScopeConfigInterface
5475 */
5476 private $scopeConfig;
5477
5478 /**
5479- * FilterStrategyInterface provides the interface to work with strategies.
5480- *
5481 * @var FilterStrategyInterface
5482 */
5483 private $filterStrategy;
5484
5485 /**
5486- * Table alias resolver for Search Request filter.
5487- *
5488 * @var AliasResolver
5489 */
5490 private $aliasResolver;
5491
5492 /**
5493- * TableMapper constructor.
5494 * @param AppResource $resource
5495 * @param StoreManagerInterface $storeManager
5496 * @param CollectionFactory $attributeCollectionFactory
5497- * @param EavConfig|null $eavConfig
5498- * @param ScopeConfigInterface|null $scopeConfig
5499- * @param FilterStrategyInterface|null $filterStrategy
5500- * @param AliasResolver|null $aliasResolver
5501+ * @param EavConfig $eavConfig
5502+ * @param ScopeConfigInterface $scopeConfig
5503+ * @param FilterStrategyInterface $filterStrategy
5504+ * @param AliasResolver $aliasResolver
5505+ *
5506+ * Added for backwards compatibility(can not delete parameter from constructor)
5507+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
5508 */
5509 public function __construct(
5510 AppResource $resource,
5511@@ -99,7 +84,6 @@ class TableMapper
5512 ) {
5513 $this->resource = $resource;
5514 $this->storeManager = $storeManager;
5515- $this->attributeCollection = $attributeCollectionFactory->create();
5516 $this->eavConfig = $eavConfig ?: ObjectManager::getInstance()->get(EavConfig::class);
5517 $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
5518 $this->filterStrategy = $filterStrategy ?: ObjectManager::getInstance()->get(FilterStrategyInterface::class);
5519@@ -107,21 +91,16 @@ class TableMapper
5520 }
5521
5522 /**
5523- * Adds tables to select.
5524- *
5525 * @param Select $select
5526 * @param RequestInterface $request
5527- *
5528 * @return Select
5529 */
5530 public function addTables(Select $select, RequestInterface $request)
5531 {
5532 $appliedFilters = [];
5533 $filters = $this->getFiltersFromQuery($request->getQuery());
5534-
5535 foreach ($filters as $filter) {
5536 $alias = $this->aliasResolver->getAlias($filter);
5537-
5538 if (!array_key_exists($alias, $appliedFilters)) {
5539 $isApplied = $this->filterStrategy->apply($filter, $select);
5540 if ($isApplied) {
5541@@ -152,7 +131,7 @@ class TableMapper
5542 * @param RequestQueryInterface $query
5543 * @return FilterInterface[]
5544 */
5545- private function getFiltersFromQuery(RequestQueryInterface $query)
5546+ private function getFiltersFromQuery(RequestQueryInterface $query)
5547 {
5548 $filters = [];
5549 switch ($query->getType()) {
5550@@ -180,6 +159,7 @@ class TableMapper
5551 default:
5552 break;
5553 }
5554+
5555 return $filters;
5556 }
5557
5558diff --git a/vendor/magento/module-catalog-search/etc/di.xml b/vendor/magento/module-catalog-search/etc/di.xml
5559index 25993adf353..dc104cc7816 100644
5560--- a/vendor/magento/module-catalog-search/etc/di.xml
5561+++ b/vendor/magento/module-catalog-search/etc/di.xml
5562@@ -12,6 +12,7 @@
5563 <preference for="Magento\Framework\Search\Dynamic\DataProviderInterface" type="Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic\DataProvider" />
5564 <preference for="Magento\Framework\Search\Adapter\OptionsInterface" type="Magento\CatalogSearch\Model\Adapter\Options" />
5565 <preference for="Magento\CatalogSearch\Model\Search\FilterMapper\FilterStrategyInterface" type="Magento\CatalogSearch\Model\Search\FilterMapper\FilterContext"/>
5566+ <preference for="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface" type="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy"/>
5567 <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory">
5568 <arguments>
5569 <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
5570@@ -20,6 +21,14 @@
5571 </argument>
5572 </arguments>
5573 </type>
5574+ <type name="Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy">
5575+ <arguments>
5576+ <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
5577+ <argument name="handlers" xsi:type="array">
5578+ <item name="mysql" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher</item>
5579+ </argument>
5580+ </arguments>
5581+ </type>
5582 <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory">
5583 <arguments>
5584 <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
5585@@ -246,4 +255,27 @@
5586 </argument>
5587 </arguments>
5588 </type>
5589+ <type name="\Magento\CatalogSearch\Model\Indexer\IndexerHandler">
5590+ <arguments>
5591+ <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
5592+ </arguments>
5593+ </type>
5594+ <type name="\Magento\CatalogSearch\Model\Indexer\IndexStructure">
5595+ <arguments>
5596+ <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
5597+ </arguments>
5598+ </type>
5599+ <type name="\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher">
5600+ <arguments>
5601+ <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
5602+ </arguments>
5603+ </type>
5604+ <type name="\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy">
5605+ <arguments>
5606+ <argument name="states" xsi:type="array">
5607+ <item name="use_temporary_table" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\TemporaryResolver</item>
5608+ <item name="use_main_table" xsi:type="string">\Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver</item>
5609+ </argument>
5610+ </arguments>
5611+ </type>
5612 </config>
5613diff --git a/vendor/magento/module-catalog-widget/Model/Rule/Condition/Product.php b/vendor/magento/module-catalog-widget/Model/Rule/Condition/Product.php
5614index 7b4ecf5ffe9..f441135e7bf 100644
5615--- a/vendor/magento/module-catalog-widget/Model/Rule/Condition/Product.php
5616+++ b/vendor/magento/module-catalog-widget/Model/Rule/Condition/Product.php
5617@@ -12,6 +12,7 @@ namespace Magento\CatalogWidget\Model\Rule\Condition;
5618
5619 /**
5620 * Class Product
5621+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
5622 */
5623 class Product extends \Magento\Rule\Model\Condition\Product\AbstractProduct
5624 {
5625diff --git a/vendor/magento/module-configurable-product/Model/ResourceModel/Indexer/Stock/Configurable.php b/vendor/magento/module-configurable-product/Model/ResourceModel/Indexer/Stock/Configurable.php
5626index ada5d90b938..0b8eaead585 100644
5627--- a/vendor/magento/module-configurable-product/Model/ResourceModel/Indexer/Stock/Configurable.php
5628+++ b/vendor/magento/module-configurable-product/Model/ResourceModel/Indexer/Stock/Configurable.php
5629@@ -13,9 +13,44 @@ namespace Magento\ConfigurableProduct\Model\ResourceModel\Indexer\Stock;
5630 * @author Magento Core Team <core@magentocommerce.com>
5631 */
5632 use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
5633+use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
5634+use Magento\Framework\App\ObjectManager;
5635
5636+/**
5637+ * Stock indexer for configurable product.
5638+ *
5639+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
5640+ */
5641 class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock
5642 {
5643+ /**
5644+ * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
5645+ */
5646+ private $activeTableSwitcher;
5647+
5648+ /**
5649+ * Configurable constructor.
5650+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
5651+ * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
5652+ * @param \Magento\Eav\Model\Config $eavConfig
5653+ * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
5654+ * @param null $connectionName
5655+ * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
5656+ */
5657+ public function __construct(
5658+ \Magento\Framework\Model\ResourceModel\Db\Context $context,
5659+ \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
5660+ \Magento\Eav\Model\Config $eavConfig,
5661+ \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
5662+ $connectionName = null,
5663+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null
5664+ ) {
5665+ parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName);
5666+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get(
5667+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class
5668+ );
5669+ }
5670+
5671 /**
5672 * Get the select object for get stock status by configurable product ids
5673 *
5674@@ -27,7 +62,10 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
5675 {
5676 $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
5677 $connection = $this->getConnection();
5678- $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable();
5679+ $table = $this->getActionType() === Full::ACTION_TYPE
5680+ ? $this->activeTableSwitcher->getAdditionalTableName($this->getMainTable())
5681+ : $this->getMainTable();
5682+ $idxTable = $usePrimaryTable ? $table : $this->getIdxTable();
5683 $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable);
5684 $select->reset(
5685 \Magento\Framework\DB\Select::COLUMNS
5686diff --git a/vendor/magento/module-configurable-product/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/vendor/magento/module-configurable-product/Model/ResourceModel/Product/Indexer/Price/Configurable.php
5687index 7f4f0f9e937..75cc5a8c958 100644
5688--- a/vendor/magento/module-configurable-product/Model/ResourceModel/Product/Indexer/Price/Configurable.php
5689+++ b/vendor/magento/module-configurable-product/Model/ResourceModel/Product/Indexer/Price/Configurable.php
5690@@ -28,7 +28,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
5691 * @param \Magento\Eav\Model\Config $eavConfig
5692 * @param \Magento\Framework\Event\ManagerInterface $eventManager
5693 * @param \Magento\Framework\Module\Manager $moduleManager
5694- * @param string $connectionName
5695+ * @param string|null $connectionName
5696 * @param StoreResolverInterface $storeResolver
5697 */
5698 public function __construct(
5699@@ -41,8 +41,9 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
5700 StoreResolverInterface $storeResolver = null
5701 ) {
5702 parent::__construct($context, $tableStrategy, $eavConfig, $eventManager, $moduleManager, $connectionName);
5703- $this->storeResolver = $storeResolver ?:
5704- \Magento\Framework\App\ObjectManager::getInstance()->get(StoreResolverInterface::class);
5705+ $this->storeResolver = $storeResolver ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
5706+ StoreResolverInterface::class
5707+ );
5708 }
5709
5710 /**
5711@@ -50,18 +51,11 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
5712 *
5713 * @return $this
5714 * @throws \Exception
5715+ * @deprecated
5716 */
5717 public function reindexAll()
5718 {
5719- $this->tableStrategy->setUseIdxTable(true);
5720- $this->beginTransaction();
5721- try {
5722- $this->reindex();
5723- $this->commit();
5724- } catch (\Exception $e) {
5725- $this->rollBack();
5726- throw $e;
5727- }
5728+ parent::reindexAll();
5729 return $this;
5730 }
5731
5732@@ -70,10 +64,11 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\
5733 *
5734 * @param int|array $entityIds
5735 * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable
5736+ * @deprecated
5737 */
5738 public function reindexEntity($entityIds)
5739 {
5740- $this->reindex($entityIds);
5741+ parent::reindexEntity($entityIds);
5742 return $this;
5743 }
5744
5745diff --git a/vendor/magento/module-configurable-product/etc/di.xml b/vendor/magento/module-configurable-product/etc/di.xml
5746index 66d6073f75d..92c9f6762d0 100644
5747--- a/vendor/magento/module-configurable-product/etc/di.xml
5748+++ b/vendor/magento/module-configurable-product/etc/di.xml
5749@@ -168,4 +168,17 @@
5750 </argument>
5751 </arguments>
5752 </type>
5753+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator">
5754+ <arguments>
5755+ <argument name="estimators" xsi:type="array">
5756+ <item name="configurable" xsi:type="object">Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement</item>
5757+ </argument>
5758+ </arguments>
5759+ </type>
5760+ <type name="Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable">
5761+ <arguments>
5762+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
5763+ <argument name="connectionName" xsi:type="string">indexer</argument>
5764+ </arguments>
5765+ </type>
5766 </config>
5767diff --git a/vendor/magento/module-downloadable/Model/ResourceModel/Indexer/Price.php b/vendor/magento/module-downloadable/Model/ResourceModel/Indexer/Price.php
5768index 843e690e342..2df2382931e 100644
5769--- a/vendor/magento/module-downloadable/Model/ResourceModel/Indexer/Price.php
5770+++ b/vendor/magento/module-downloadable/Model/ResourceModel/Indexer/Price.php
5771@@ -19,18 +19,11 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D
5772 *
5773 * @throws \Exception
5774 * @return $this
5775+ * @deprecated
5776 */
5777 public function reindexAll()
5778 {
5779- $this->tableStrategy->setUseIdxTable(true);
5780- $this->beginTransaction();
5781- try {
5782- $this->reindex();
5783- $this->commit();
5784- } catch (\Exception $e) {
5785- $this->rollBack();
5786- throw $e;
5787- }
5788+ parent::reindexAll();
5789 return $this;
5790 }
5791
5792@@ -39,10 +32,12 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D
5793 *
5794 * @param int|array $entityIds
5795 * @return $this
5796+ * @deprecated
5797 */
5798 public function reindexEntity($entityIds)
5799 {
5800- return $this->reindex($entityIds);
5801+ parent::reindexEntity($entityIds);
5802+ return $this;
5803 }
5804
5805 /**
5806diff --git a/vendor/magento/module-downloadable/etc/di.xml b/vendor/magento/module-downloadable/etc/di.xml
5807index 84b6de602ac..d399806bfba 100644
5808--- a/vendor/magento/module-downloadable/etc/di.xml
5809+++ b/vendor/magento/module-downloadable/etc/di.xml
5810@@ -123,4 +123,10 @@
5811 </argument>
5812 </arguments>
5813 </type>
5814+ <type name="Magento\Downloadable\Model\ResourceModel\Indexer\Price">
5815+ <arguments>
5816+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
5817+ <argument name="connectionName" xsi:type="string">indexer</argument>
5818+ </arguments>
5819+ </type>
5820 </config>
5821diff --git a/vendor/magento/module-grouped-product/Model/ResourceModel/Indexer/Stock/Grouped.php b/vendor/magento/module-grouped-product/Model/ResourceModel/Indexer/Stock/Grouped.php
5822index 740dec1aa27..c17597ab252 100644
5823--- a/vendor/magento/module-grouped-product/Model/ResourceModel/Indexer/Stock/Grouped.php
5824+++ b/vendor/magento/module-grouped-product/Model/ResourceModel/Indexer/Stock/Grouped.php
5825@@ -12,9 +12,44 @@
5826 namespace Magento\GroupedProduct\Model\ResourceModel\Indexer\Stock;
5827
5828 use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
5829+use Magento\Framework\App\ObjectManager;
5830+use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
5831
5832+/**
5833+ * Stock indexer for grouped product.
5834+ *
5835+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
5836+ */
5837 class Grouped extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock
5838 {
5839+ /**
5840+ * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
5841+ */
5842+ private $activeTableSwitcher;
5843+
5844+ /**
5845+ * Grouped constructor.
5846+ * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
5847+ * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy
5848+ * @param \Magento\Eav\Model\Config $eavConfig
5849+ * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
5850+ * @param null $connectionName
5851+ * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
5852+ */
5853+ public function __construct(
5854+ \Magento\Framework\Model\ResourceModel\Db\Context $context,
5855+ \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
5856+ \Magento\Eav\Model\Config $eavConfig,
5857+ \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
5858+ $connectionName = null,
5859+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null
5860+ ) {
5861+ parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName);
5862+ $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get(
5863+ \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class
5864+ );
5865+ }
5866+
5867 /**
5868 * Get the select object for get stock status by grouped product ids
5869 *
5870@@ -25,7 +60,10 @@ class Grouped extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stoc
5871 protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false)
5872 {
5873 $connection = $this->getConnection();
5874- $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable();
5875+ $table = $this->getActionType() === Full::ACTION_TYPE
5876+ ? $this->activeTableSwitcher->getAdditionalTableName($this->getMainTable())
5877+ : $this->getMainTable();
5878+ $idxTable = $usePrimaryTable ? $table : $this->getIdxTable();
5879 $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
5880 $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable);
5881 $select->reset(
5882diff --git a/vendor/magento/module-grouped-product/Model/ResourceModel/Product/Indexer/Price/Grouped.php b/vendor/magento/module-grouped-product/Model/ResourceModel/Product/Indexer/Price/Grouped.php
5883index eaed75a967c..55882b40b47 100644
5884--- a/vendor/magento/module-grouped-product/Model/ResourceModel/Product/Indexer/Price/Grouped.php
5885+++ b/vendor/magento/module-grouped-product/Model/ResourceModel/Product/Indexer/Price/Grouped.php
5886@@ -12,23 +12,21 @@ use Magento\Catalog\Api\Data\ProductInterface;
5887
5888 class Grouped extends DefaultPrice implements GroupedInterface
5889 {
5890+ /**
5891+ * Prefix for temporary table support.
5892+ */
5893+ const TRANSIT_PREFIX = 'transit_';
5894+
5895 /**
5896 * Reindex temporary (price result data) for all products
5897 *
5898 * @throws \Exception
5899 * @return \Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped
5900+ * @deprecated
5901 */
5902 public function reindexAll()
5903 {
5904- $this->tableStrategy->setUseIdxTable(true);
5905- $this->beginTransaction();
5906- try {
5907- $this->_prepareGroupedProductPriceData();
5908- $this->commit();
5909- } catch (\Exception $e) {
5910- $this->rollBack();
5911- throw $e;
5912- }
5913+ parent::reindexAll();
5914 return $this;
5915 }
5916
5917@@ -37,26 +35,72 @@ class Grouped extends DefaultPrice implements GroupedInterface
5918 *
5919 * @param int|array $entityIds
5920 * @return \Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped
5921+ * @deprecated
5922 */
5923 public function reindexEntity($entityIds)
5924 {
5925- $this->_prepareGroupedProductPriceData($entityIds);
5926-
5927+ parent::reindexEntity($entityIds);
5928 return $this;
5929 }
5930
5931+ /**
5932+ * @inheritdoc
5933+ */
5934+ protected function reindex($entityIds = null)
5935+ {
5936+ $this->_prepareGroupedProductPriceData($entityIds);
5937+ }
5938+
5939 /**
5940 * Calculate minimal and maximal prices for Grouped products
5941 * Use calculated price for relation products
5942 *
5943 * @param int|array $entityIds the parent entity ids limitation
5944- * @return \Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped
5945+ * @return $this
5946 */
5947 protected function _prepareGroupedProductPriceData($entityIds = null)
5948 {
5949 if (!$this->hasEntity() && empty($entityIds)) {
5950 return $this;
5951 }
5952+
5953+ $connection = $this->getConnection();
5954+ $table = $this->getIdxTable();
5955+
5956+ if (!$this->tableStrategy->getUseIdxTable()) {
5957+ $additionalIdxTable = $connection->getTableName(self::TRANSIT_PREFIX . $this->getIdxTable());
5958+ $connection->createTemporaryTableLike($additionalIdxTable, $table);
5959+ $query = $connection->insertFromSelect(
5960+ $this->_prepareGroupedProductPriceDataSelect($entityIds),
5961+ $additionalIdxTable,
5962+ []
5963+ );
5964+ $connection->query($query);
5965+
5966+ $select = $connection->select()->from($additionalIdxTable);
5967+ $query = $connection->insertFromSelect(
5968+ $select,
5969+ $table,
5970+ [],
5971+ \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE
5972+ );
5973+ $connection->query($query);
5974+ $connection->dropTemporaryTable($additionalIdxTable);
5975+ } else {
5976+ $query = $this->_prepareGroupedProductPriceDataSelect($entityIds)->insertFromSelect($table);
5977+ $connection->query($query);
5978+ }
5979+ return $this;
5980+ }
5981+
5982+ /**
5983+ * Prepare data index select for Grouped products prices
5984+ *
5985+ * @param int|array $entityIds the parent entity ids limitation
5986+ * @return \Magento\Framework\DB\Select
5987+ */
5988+ protected function _prepareGroupedProductPriceDataSelect($entityIds = null)
5989+ {
5990 $connection = $this->getConnection();
5991 $table = $this->getIdxTable();
5992 $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
5993@@ -123,10 +167,6 @@ class Grouped extends DefaultPrice implements GroupedInterface
5994 'store_field' => new \Zend_Db_Expr('cs.store_id')
5995 ]
5996 );
5997-
5998- $query = $select->insertFromSelect($table);
5999- $connection->query($query);
6000-
6001- return $this;
6002+ return $select;
6003 }
6004 }
6005diff --git a/vendor/magento/module-grouped-product/etc/di.xml b/vendor/magento/module-grouped-product/etc/di.xml
6006index 788052d07fc..17c9a644b7b 100644
6007--- a/vendor/magento/module-grouped-product/etc/di.xml
6008+++ b/vendor/magento/module-grouped-product/etc/di.xml
6009@@ -89,4 +89,11 @@
6010 </type>
6011 <preference for="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\GroupedInterface"
6012 type="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped"/>
6013+ <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator">
6014+ <arguments>
6015+ <argument name="estimators" xsi:type="array">
6016+ <item name="grouped" xsi:type="object">Magento\Catalog\Model\Indexer\Price\CompositeProductBatchSizeManagement</item>
6017+ </argument>
6018+ </arguments>
6019+ </type>
6020 </config>
6021diff --git a/vendor/magento/module-indexer/Model/Indexer.php b/vendor/magento/module-indexer/Model/Indexer.php
6022index feea37a2636..e799ab04a71 100644
6023--- a/vendor/magento/module-indexer/Model/Indexer.php
6024+++ b/vendor/magento/module-indexer/Model/Indexer.php
6025@@ -59,6 +59,7 @@ class Indexer extends \Magento\Framework\DataObject implements IdxInterface
6026 protected $indexersFactory;
6027
6028 /**
6029+ * Indexer constructor.
6030 * @param ConfigInterface $config
6031 * @param ActionFactory $actionFactory
6032 * @param StructureFactory $structureFactory
6033@@ -413,7 +414,7 @@ class Indexer extends \Magento\Framework\DataObject implements IdxInterface
6034 $state->setStatus(StateInterface::STATUS_VALID);
6035 $state->save();
6036 $this->getView()->resume();
6037- } catch (\Exception $exception) {
6038+ } catch (\Throwable $exception) {
6039 $state->setStatus(StateInterface::STATUS_INVALID);
6040 $state->save();
6041 $this->getView()->resume();
6042@@ -436,7 +437,7 @@ class Indexer extends \Magento\Framework\DataObject implements IdxInterface
6043
6044 /**
6045 * Regenerate rows in index by ID list
6046- *5
6047+ *
6048 * @param int[] $ids
6049 * @return void
6050 */
6051diff --git a/vendor/magento/module-indexer/etc/di.xml b/vendor/magento/module-indexer/etc/di.xml
6052index 1d3f125406f..7cb8971dfda 100644
6053--- a/vendor/magento/module-indexer/etc/di.xml
6054+++ b/vendor/magento/module-indexer/etc/di.xml
6055@@ -55,4 +55,5 @@
6056 </argument>
6057 </arguments>
6058 </type>
6059+ <preference for="Magento\Framework\Indexer\BatchSizeManagementInterface" type="Magento\Framework\Indexer\BatchSizeManagement" />
6060 </config>
6061diff --git a/vendor/magento/module-indexer/etc/module.xml b/vendor/magento/module-indexer/etc/module.xml
6062index a2beac15990..27c6a47d56a 100644
6063--- a/vendor/magento/module-indexer/etc/module.xml
6064+++ b/vendor/magento/module-indexer/etc/module.xml
6065@@ -6,7 +6,7 @@
6066 */
6067 -->
6068 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
6069- <module name="Magento_Indexer" setup_version="2.0.0">
6070+ <module name="Magento_Indexer" setup_version="2.0.1.1.2">
6071 <sequence>
6072 <module name="Magento_Store"/>
6073 <module name="Magento_AdminNotification"/>
6074diff --git a/vendor/magento/framework/Indexer/BatchProvider.php b/vendor/magento/framework/Indexer/BatchProvider.php
6075new file mode 100644
6076index 00000000000..9f8a1b1ace4
6077--- /dev/null
6078+++ b/vendor/magento/framework/Indexer/BatchProvider.php
6079@@ -0,0 +1,64 @@
6080+<?php
6081+/**
6082+ * Copyright © Magento, Inc. All rights reserved.
6083+ * See COPYING.txt for license details.
6084+ */
6085+namespace Magento\Framework\Indexer;
6086+
6087+use \Magento\Framework\DB\Adapter\AdapterInterface;
6088+
6089+/**
6090+ * Generator of consecutive entity ID ranges that must be handled as a batch.
6091+ *
6092+ * Some ranges may contain non existent entity IDs.
6093+ * So the code that uses the generator must check if any entities were loaded during batch load.
6094+ */
6095+class BatchProvider implements BatchProviderInterface
6096+{
6097+ /**
6098+ * @inheritdoc
6099+ */
6100+ public function getBatches(AdapterInterface $adapter, $tableName, $linkField, $batchSize)
6101+ {
6102+ $maxLinkFieldValue = $adapter->fetchOne(
6103+ $adapter->select()->from(
6104+ ['entity' => $tableName],
6105+ [
6106+ 'max_value' => new \Zend_Db_Expr('MAX(entity.' . $linkField . ')')
6107+ ]
6108+ )
6109+ );
6110+
6111+ /** @var int $truncatedBatchSize size of the last batch that is smaller than expected batch size */
6112+ $truncatedBatchSize = $maxLinkFieldValue % $batchSize;
6113+ /** @var int $fullBatchCount count of the batches that have expected batch size */
6114+ $fullBatchCount = ($maxLinkFieldValue - $truncatedBatchSize) / $batchSize;
6115+
6116+ for ($batchIndex = 0; $batchIndex < $fullBatchCount; $batchIndex ++) {
6117+ yield ['from' => $batchIndex * $batchSize + 1, 'to' => ($batchIndex + 1) * $batchSize];
6118+ }
6119+ // return the last batch if it has smaller size
6120+ if ($truncatedBatchSize > 0) {
6121+ yield ['from' => $fullBatchCount * $batchSize + 1, 'to' => $maxLinkFieldValue];
6122+ }
6123+ }
6124+
6125+ /**
6126+ * @inheritdoc
6127+ */
6128+ public function getBatchIds(
6129+ \Magento\Framework\DB\Adapter\AdapterInterface $connection,
6130+ \Magento\Framework\DB\Select $select,
6131+ array $batch
6132+ ) {
6133+ $betweenCondition = sprintf(
6134+ '(%s BETWEEN %s AND %s)',
6135+ 'entity_id',
6136+ $connection->quote($batch['from']),
6137+ $connection->quote($batch['to'])
6138+ );
6139+
6140+ $ids = $connection->fetchCol($select->where($betweenCondition));
6141+ return array_map('intval', $ids);
6142+ }
6143+}
6144diff --git a/vendor/magento/framework/Indexer/BatchProviderInterface.php b/vendor/magento/framework/Indexer/BatchProviderInterface.php
6145new file mode 100644
6146index 00000000000..09972f48bfd
6147--- /dev/null
6148+++ b/vendor/magento/framework/Indexer/BatchProviderInterface.php
6149@@ -0,0 +1,39 @@
6150+<?php
6151+/**
6152+ * Copyright © Magento, Inc. All rights reserved.
6153+ * See COPYING.txt for license details.
6154+ */
6155+namespace Magento\Framework\Indexer;
6156+
6157+use Magento\Framework\DB\Adapter\AdapterInterface;
6158+use Magento\Framework\DB\Select;
6159+
6160+/**
6161+ * Generator of consecutive entity ID ranges that must be handled as a batch.
6162+ *
6163+ * Can be used during indexation process to split large amount of data into batches
6164+ * and process them one by one in order to reduce memory consumption and improve overall performance.
6165+ */
6166+interface BatchProviderInterface
6167+{
6168+ /**
6169+ * Retrieve batches (entity ID ranges) from the given table.
6170+ *
6171+ * @param AdapterInterface $adapter database adapter.
6172+ * @param string $tableName target table name.
6173+ * @param string $linkField field that is used as a record identifier.
6174+ * @param int $batchSize size of the single range.
6175+ * @return \Generator generator that produces entity ID ranges in the format of ['from' => ..., 'to' => ...]
6176+ */
6177+ public function getBatches(AdapterInterface $adapter, $tableName, $linkField, $batchSize);
6178+
6179+ /**
6180+ * Get list of entity ids based on batch
6181+ *
6182+ * @param AdapterInterface $connection
6183+ * @param Select $select
6184+ * @param array $batch
6185+ * @return array
6186+ */
6187+ public function getBatchIds(AdapterInterface $connection, Select $select, array $batch);
6188+}
6189diff --git a/vendor/magento/framework/Indexer/BatchSizeManagement.php b/vendor/magento/framework/Indexer/BatchSizeManagement.php
6190new file mode 100644
6191index 00000000000..2733aae4bd5
6192--- /dev/null
6193+++ b/vendor/magento/framework/Indexer/BatchSizeManagement.php
6194@@ -0,0 +1,65 @@
6195+<?php
6196+/**
6197+ * Copyright © Magento, Inc. All rights reserved.
6198+ * See COPYING.txt for license details.
6199+ */
6200+
6201+namespace Magento\Framework\Indexer;
6202+
6203+/**
6204+ * Class set MEMORY table size for indexer processes according batch size and index row size.
6205+ */
6206+class BatchSizeManagement implements \Magento\Framework\Indexer\BatchSizeManagementInterface
6207+{
6208+ /**
6209+ * @var \Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface
6210+ */
6211+ private $rowSizeEstimator;
6212+
6213+ /**
6214+ * @var \Psr\Log\LoggerInterface
6215+ */
6216+ private $logger;
6217+
6218+ /**
6219+ * CompositeProductBatchSizeCalculator constructor.
6220+ * @param \Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface $rowSizeEstimator
6221+ * @param \Psr\Log\LoggerInterface $logger
6222+ */
6223+ public function __construct(
6224+ \Magento\Framework\Indexer\IndexTableRowSizeEstimatorInterface $rowSizeEstimator,
6225+ \Psr\Log\LoggerInterface $logger
6226+ ) {
6227+ $this->rowSizeEstimator = $rowSizeEstimator;
6228+ $this->logger = $logger;
6229+ }
6230+
6231+ /**
6232+ * @inheritdoc
6233+ */
6234+ public function ensureBatchSize(\Magento\Framework\DB\Adapter\AdapterInterface $connection, $batchSize)
6235+ {
6236+ $rowMemory = $this->rowSizeEstimator->estimateRowSize();
6237+
6238+ $maxHeapTableSize = $connection->fetchOne('SELECT @@max_heap_table_size;');
6239+ $tmpTableSize = $connection->fetchOne('SELECT @@tmp_table_size;');
6240+ $bufferPoolSize = $connection->fetchOne('SELECT @@innodb_buffer_pool_size;');
6241+ $maxMemoryTableSize = min($maxHeapTableSize, $tmpTableSize);
6242+
6243+ $size = (int) ($rowMemory * $batchSize);
6244+
6245+ // Log warning if allocated memory for temp table greater than 20% of innodb_buffer_pool_size
6246+ if ($size > $bufferPoolSize * .2) {
6247+ $message = 'Memory size allocated for the temporary table is more than 20% of innodb_buffer_pool_size. ' .
6248+ 'Please update innodb_buffer_pool_size or decrease batch size value '.
6249+ '(which decreases memory usages for the temporary table). ' .
6250+ 'Current batch size: %1; Allocated memory size: %2 bytes; InnoDB buffer pool size: %3 bytes.';
6251+ $this->logger->warning(new \Magento\Framework\Phrase($message, [$batchSize, $size, $bufferPoolSize]));
6252+ }
6253+
6254+ if ($maxMemoryTableSize < $size) {
6255+ $connection->query('SET SESSION tmp_table_size = ' . $size . ';');
6256+ $connection->query('SET SESSION max_heap_table_size = ' . $size . ';');
6257+ }
6258+ }
6259+}
6260diff --git a/vendor/magento/framework/Indexer/BatchSizeManagementInterface.php b/vendor/magento/framework/Indexer/BatchSizeManagementInterface.php
6261new file mode 100644
6262index 00000000000..ac5b5dae291
6263--- /dev/null
6264+++ b/vendor/magento/framework/Indexer/BatchSizeManagementInterface.php
6265@@ -0,0 +1,24 @@
6266+<?php
6267+/**
6268+ * Copyright © Magento, Inc. All rights reserved.
6269+ * See COPYING.txt for license details.
6270+ */
6271+namespace Magento\Framework\Indexer;
6272+
6273+use \Magento\Framework\DB\Adapter\AdapterInterface;
6274+
6275+/**
6276+ * Batch size manager can be used to ensure that MEMORY table has enough memory for data in batch.
6277+ * @api
6278+ */
6279+interface BatchSizeManagementInterface
6280+{
6281+ /**
6282+ * Ensure memory size for data in batch.
6283+ *
6284+ * @param AdapterInterface $adapter database adapter.
6285+ * @param int $batchSize
6286+ * @return void
6287+ */
6288+ public function ensureBatchSize(\Magento\Framework\DB\Adapter\AdapterInterface $adapter, $batchSize);
6289+}
6290diff --git a/vendor/magento/framework/Indexer/IndexTableRowSizeEstimator.php b/vendor/magento/framework/Indexer/IndexTableRowSizeEstimator.php
6291new file mode 100644
6292index 00000000000..d26c85a6d02
6293--- /dev/null
6294+++ b/vendor/magento/framework/Indexer/IndexTableRowSizeEstimator.php
6295@@ -0,0 +1,34 @@
6296+<?php
6297+/**
6298+ * Copyright © Magento, Inc. All rights reserved.
6299+ * See COPYING.txt for license details.
6300+ */
6301+
6302+namespace Magento\Framework\Indexer;
6303+
6304+/**
6305+ * Generic implementation for row size estimation.
6306+ */
6307+class IndexTableRowSizeEstimator implements IndexTableRowSizeEstimatorInterface
6308+{
6309+ /**
6310+ * @var int
6311+ */
6312+ private $rowMemorySize;
6313+
6314+ /**
6315+ * @param int $rowMemorySize
6316+ */
6317+ public function __construct($rowMemorySize)
6318+ {
6319+ $this->rowMemorySize = $rowMemorySize;
6320+ }
6321+
6322+ /**
6323+ * @inheritdoc
6324+ */
6325+ public function estimateRowSize()
6326+ {
6327+ return $this->rowMemorySize;
6328+ }
6329+}
6330diff --git a/vendor/magento/framework/Indexer/IndexTableRowSizeEstimatorInterface.php b/vendor/magento/framework/Indexer/IndexTableRowSizeEstimatorInterface.php
6331new file mode 100644
6332index 00000000000..c2c25ef4969
6333--- /dev/null
6334+++ b/vendor/magento/framework/Indexer/IndexTableRowSizeEstimatorInterface.php
6335@@ -0,0 +1,21 @@
6336+<?php
6337+/**
6338+ * Copyright © Magento, Inc. All rights reserved.
6339+ * See COPYING.txt for license details.
6340+ */
6341+
6342+namespace Magento\Framework\Indexer;
6343+
6344+/**
6345+ * Calculate memory size for entity according different dimensions.
6346+ * @api
6347+ */
6348+interface IndexTableRowSizeEstimatorInterface
6349+{
6350+ /**
6351+ * Calculate memory size for entity row.
6352+ *
6353+ * @return float
6354+ */
6355+ public function estimateRowSize();
6356+}
6357diff --git a/vendor/magento/framework/Indexer/ScopeResolver/IndexScopeResolver.php b/vendor/magento/framework/Indexer/ScopeResolver/IndexScopeResolver.php
6358index 20365b399fc..77f2c7a1b64 100644
6359--- a/vendor/magento/framework/Indexer/ScopeResolver/IndexScopeResolver.php
6360+++ b/vendor/magento/framework/Indexer/ScopeResolver/IndexScopeResolver.php
6361@@ -52,6 +52,7 @@ class IndexScopeResolver implements IndexScopeResolverInterface
6362 $tableNameParts[] = $dimension->getName() . $dimension->getValue();
6363 }
6364 }
6365+
6366 return $this->resource->getTableName(implode('_', $tableNameParts));
6367 }
6368
6369@@ -63,10 +64,12 @@ class IndexScopeResolver implements IndexScopeResolverInterface
6370 */
6371 private function getScopeId($dimension)
6372 {
6373- if (is_numeric($dimension->getValue())) {
6374- return $dimension->getValue();
6375- } else {
6376- return $this->scopeResolver->getScope($dimension->getValue())->getId();
6377+ $scopeId = $dimension->getValue();
6378+
6379+ if (!is_numeric($scopeId)) {
6380+ $scopeId = $this->scopeResolver->getScope($scopeId)->getId();
6381 }
6382+
6383+ return $scopeId;
6384 }
6385 }
6386diff --git a/vendor/magento/framework/Search/Adapter/Mysql/Adapter.php b/vendor/magento/framework/Search/Adapter/Mysql/Adapter.php
6387index b348d5a454b..d89b7eaa13d 100644
6388--- a/vendor/magento/framework/Search/Adapter/Mysql/Adapter.php
6389+++ b/vendor/magento/framework/Search/Adapter/Mysql/Adapter.php
6390@@ -70,12 +70,7 @@ class Adapter implements AdapterInterface
6391 }
6392
6393 /**
6394- * Process Search Request.
6395- *
6396- * @param RequestInterface $request
6397- *
6398- * @return \Magento\Framework\Search\Response\QueryResponse
6399- *
6400+ * {@inheritdoc}
6401 * @throws \LogicException
6402 */
6403 public function query(RequestInterface $request)
6404diff --git a/vendor/magento/framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php b/vendor/magento/framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php
6405index 239bb22ee77..864dfc45b2c 100644
6406--- a/vendor/magento/framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php
6407+++ b/vendor/magento/framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php
6408@@ -10,14 +10,14 @@ use Magento\Framework\Search\Request\BucketInterface as RequestBucketInterface;
6409 class Metrics
6410 {
6411 /**
6412- * Available metrics.
6413+ * Available metrics
6414 *
6415 * @var string[]
6416 */
6417 private $allowedMetrics = ['count', 'sum', 'min', 'max', 'avg'];
6418
6419 /**
6420- * Build metrics for Select->columns.
6421+ * Build metrics for Select->columns
6422 *
6423 * @param RequestBucketInterface $bucket
6424 * @return string[]
6425diff --git a/vendor/magento/module-advanced-catalog/Model/Indexer/Table/Strategy.php b/vendor/magento/module-advanced-catalog/Model/Indexer/Table/Strategy.php
6426index 0da6535c26e..3c25541c924 100644
6427--- a/vendor/magento/module-advanced-catalog/Model/Indexer/Table/Strategy.php
6428+++ b/vendor/magento/module-advanced-catalog/Model/Indexer/Table/Strategy.php
6429@@ -5,15 +5,39 @@
6430 */
6431 namespace Magento\AdvancedCatalog\Model\Indexer\Table;
6432
6433+use Magento\Framework\App\ObjectManager;
6434+
6435 /**
6436 * Class Strategy
6437 *
6438 * @author Magento Core Team <core@magentocommerce.com>
6439+ * @deprecated logic moved to catalog module
6440+ * @see \Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy
6441 */
6442 class Strategy extends \Magento\Framework\Indexer\Table\Strategy
6443 {
6444 const TEMP_SUFFIX = '_temp';
6445
6446+ /**
6447+ * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy
6448+ */
6449+ private $tableStrategy;
6450+
6451+ /**
6452+ * Strategy constructor.
6453+ * @param \Magento\Framework\App\ResourceConnection $resource
6454+ * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy|null $tableStrategy
6455+ */
6456+ public function __construct(
6457+ \Magento\Framework\App\ResourceConnection $resource,
6458+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy $tableStrategy = null
6459+ ) {
6460+ parent::__construct($resource);
6461+ $this->tableStrategy = $tableStrategy ?: ObjectManager::getInstance()->get(
6462+ \Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy::class
6463+ );
6464+ }
6465+
6466 /**
6467 * Prepare index table name
6468 *
6469@@ -23,15 +47,6 @@ class Strategy extends \Magento\Framework\Indexer\Table\Strategy
6470 */
6471 public function prepareTableName($tablePrefix)
6472 {
6473- if ($this->getUseIdxTable()) {
6474- return $tablePrefix . self::IDX_SUFFIX;
6475- } else {
6476- $this->resource->getConnection('indexer')->createTemporaryTableLike(
6477- $this->resource->getTableName($tablePrefix . self::TEMP_SUFFIX),
6478- $this->resource->getTableName($tablePrefix . self::TMP_SUFFIX),
6479- true
6480- );
6481- return $tablePrefix . self::TEMP_SUFFIX;
6482- }
6483+ return $this->tableStrategy->prepareTableName($tablePrefix);
6484 }
6485 }
6486diff --git a/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php b/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
6487index 478b195cef7..f4dee8e8b95 100644
6488--- a/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
6489+++ b/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php
6490@@ -8,53 +8,21 @@ namespace Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Eav;
6491 /**
6492 * Catalog Product Eav Select and Multiply Select Attributes Indexer resource model
6493 *
6494- * @author Magento Core Team <core@magentocommerce.com>
6495+ * @deprecated Logic moved to parent class implementation
6496+ * @see \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source
6497 */
6498 class Source extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source
6499 {
6500- const TRANSIT_PREFIX = 'transit_';
6501-
6502 /**
6503 * Prepare data index for product relations
6504 *
6505 * @param array $parentIds the parent entity ids limitation
6506 * @return $this
6507+ * @deprecated
6508 */
6509 protected function _prepareRelationIndex($parentIds = null)
6510 {
6511- $connection = $this->getConnection();
6512- $idxTable = $this->getIdxTable();
6513-
6514- if (!$this->tableStrategy->getUseIdxTable()) {
6515- $additionalIdxTable = $connection->getTableName(self::TRANSIT_PREFIX . $this->getIdxTable());
6516- $connection->createTemporaryTableLike($additionalIdxTable, $idxTable);
6517-
6518- $query = $connection->insertFromSelect(
6519- $this->_prepareRelationIndexSelect($parentIds),
6520- $additionalIdxTable,
6521- []
6522- );
6523- $connection->query($query);
6524-
6525- $select = $connection->select()->from($additionalIdxTable);
6526- $query = $connection->insertFromSelect(
6527- $select,
6528- $idxTable,
6529- [],
6530- \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
6531- );
6532- $connection->query($query);
6533-
6534- $connection->dropTemporaryTable($additionalIdxTable);
6535- } else {
6536- $query = $connection->insertFromSelect(
6537- $this->_prepareRelationIndexSelect($parentIds),
6538- $idxTable,
6539- [],
6540- \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
6541- );
6542- $connection->query($query);
6543- }
6544+ parent::_prepareRelationIndex($parentIds);
6545 return $this;
6546 }
6547 }
6548diff --git a/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Price/Grouped.php b/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Price/Grouped.php
6549index 5f71b385826..f9b564a2876 100644
6550--- a/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Price/Grouped.php
6551+++ b/vendor/magento/module-advanced-catalog/Model/ResourceModel/Product/Indexer/Price/Grouped.php
6552@@ -9,44 +9,15 @@ namespace Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Price;
6553
6554 use Magento\Catalog\Api\Data\ProductInterface;
6555
6556+/**
6557+ * @deprecated logic moved to default price indexer.
6558+ * @see \Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped
6559+ */
6560 class Grouped extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice
6561 {
6562 const TRANSIT_PREFIX = 'transit_';
6563 const LINK_TYPE_GROUPED = 3;
6564
6565- /**
6566- * Reindex temporary (price result data) for all products
6567- *
6568- * @throws \Exception
6569- * @return $this
6570- */
6571- public function reindexAll()
6572- {
6573- $this->tableStrategy->setUseIdxTable(true);
6574- $this->beginTransaction();
6575- try {
6576- $this->_prepareGroupedProductPriceData();
6577- $this->commit();
6578- } catch (\Exception $e) {
6579- $this->rollBack();
6580- throw $e;
6581- }
6582- return $this;
6583- }
6584-
6585- /**
6586- * Reindex temporary (price result data) for defined product(s)
6587- *
6588- * @param int|array $entityIds
6589- * @return $this
6590- */
6591- public function reindexEntity($entityIds)
6592- {
6593- $this->_prepareGroupedProductPriceData($entityIds);
6594-
6595- return $this;
6596- }
6597-
6598 /**
6599 * Calculate minimal and maximal prices for Grouped products
6600 * Use calculated price for relation products
6601@@ -165,4 +136,12 @@ class Grouped extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price
6602 );
6603 return $select;
6604 }
6605+
6606+ /**
6607+ * @inheritdoc
6608+ */
6609+ protected function reindex($entityIds = null)
6610+ {
6611+ $this->_prepareGroupedProductPriceData($entityIds);
6612+ }
6613 }
6614diff --git a/vendor/magento/module-advanced-catalog/etc/di.xml b/vendor/magento/module-advanced-catalog/etc/di.xml
6615index 5a8f894d3e5..7aef529f80c 100644
6616--- a/vendor/magento/module-advanced-catalog/etc/di.xml
6617+++ b/vendor/magento/module-advanced-catalog/etc/di.xml
6618@@ -7,48 +7,4 @@
6619 -->
6620 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
6621 <type name="Magento\AdvancedCatalog\Model\Indexer\Table\Strategy" shared="false" />
6622- <preference for="Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source" type="Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Eav\Source" />
6623- <type name="Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Eav\Source">
6624- <arguments>
6625- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6626- <argument name="connectionName" xsi:type="string">indexer</argument>
6627- </arguments>
6628- </type>
6629- <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice">
6630- <arguments>
6631- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6632- <argument name="connectionName" xsi:type="string">indexer</argument>
6633- </arguments>
6634- </type>
6635- <preference for="Magento\GroupedProduct\Model\ResourceModel\Product\Indexer\Price\Grouped" type="Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Price\Grouped" />
6636- <type name="Magento\AdvancedCatalog\Model\ResourceModel\Product\Indexer\Price\Grouped">
6637- <arguments>
6638- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6639- <argument name="connectionName" xsi:type="string">indexer</argument>
6640- </arguments>
6641- </type>
6642- <type name="Magento\Bundle\Model\ResourceModel\Indexer\Price">
6643- <arguments>
6644- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6645- <argument name="connectionName" xsi:type="string">indexer</argument>
6646- </arguments>
6647- </type>
6648- <type name="Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable">
6649- <arguments>
6650- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6651- <argument name="connectionName" xsi:type="string">indexer</argument>
6652- </arguments>
6653- </type>
6654- <type name="Magento\Downloadable\Model\ResourceModel\Indexer\Price">
6655- <arguments>
6656- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6657- <argument name="connectionName" xsi:type="string">indexer</argument>
6658- </arguments>
6659- </type>
6660- <type name="Magento\GiftCard\Model\ResourceModel\Indexer\Price">
6661- <arguments>
6662- <argument name="tableStrategy" xsi:type="object">Magento\AdvancedCatalog\Model\Indexer\Table\Strategy</argument>
6663- <argument name="connectionName" xsi:type="string">indexer</argument>
6664- </arguments>
6665- </type>
6666 </config>
6667diff --git a/vendor/magento/module-advanced-search/Model/ResourceModel/Index.php b/vendor/magento/module-advanced-search/Model/ResourceModel/Index.php
6668index 8f7804fba62..6eb9f34d8c7 100644
6669--- a/vendor/magento/module-advanced-search/Model/ResourceModel/Index.php
6670+++ b/vendor/magento/module-advanced-search/Model/ResourceModel/Index.php
6671@@ -36,9 +36,9 @@ class Index extends AbstractDb
6672 MetadataPool $metadataPool,
6673 $connectionName = null
6674 ) {
6675+ parent::__construct($context, $connectionName);
6676 $this->storeManager = $storeManager;
6677 $this->metadataPool = $metadataPool;
6678- parent::__construct($context, $connectionName);
6679 }
6680
6681 /**
6682diff --git a/vendor/magento/module-catalog-rule-staging/Setup/UpgradeSchema.php b/vendor/magento/module-catalog-rule-staging/Setup/UpgradeSchema.php
6683new file mode 100644
6684index 00000000000..768f22beb3b
6685--- /dev/null
6686+++ b/vendor/magento/module-catalog-rule-staging/Setup/UpgradeSchema.php
6687@@ -0,0 +1,36 @@
6688+<?php
6689+/**
6690+ * Copyright © Magento, Inc. All rights reserved.
6691+ * See COPYING.txt for license details.
6692+ */
6693+
6694+namespace Magento\CatalogRuleStaging\Setup;
6695+
6696+use Magento\Framework\Setup\UpgradeSchemaInterface;
6697+use Magento\Framework\Setup\ModuleContextInterface;
6698+use Magento\Framework\Setup\SchemaSetupInterface;
6699+
6700+class UpgradeSchema implements UpgradeSchemaInterface
6701+{
6702+ /**
6703+ * {@inheritdoc}
6704+ */
6705+ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
6706+ {
6707+ $setup->startSetup();
6708+
6709+ if (version_compare($context->getVersion(), '2.0.0.1.2', '<')) {
6710+ $connection = $setup->getConnection();
6711+ $connection->dropForeignKey(
6712+ $setup->getTable('catalogrule_group_website'),
6713+ $setup->getFkName(
6714+ 'catalogrule_group_website',
6715+ 'rule_id',
6716+ 'sequence_catalogrule',
6717+ 'sequence_value'
6718+ )
6719+ );
6720+ }
6721+ $setup->endSetup();
6722+ }
6723+}
6724diff --git a/vendor/magento/module-catalog-rule-staging/etc/module.xml b/vendor/magento/module-catalog-rule-staging/etc/module.xml
6725index d357a65a2ff..547e0a9659c 100644
6726--- a/vendor/magento/module-catalog-rule-staging/etc/module.xml
6727+++ b/vendor/magento/module-catalog-rule-staging/etc/module.xml
6728@@ -6,7 +6,7 @@
6729 */
6730 -->
6731 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
6732- <module name="Magento_CatalogRuleStaging" setup_version="2.0.0">
6733+ <module name="Magento_CatalogRuleStaging" setup_version="2.0.0.1.2">
6734 <sequence>
6735 <module name="Magento_Staging"/>
6736 <module name="Magento_CatalogRule"/>
6737diff --git a/vendor/magento/module-catalog-staging/Setup/UpgradeSchema.php b/vendor/magento/module-catalog-staging/Setup/UpgradeSchema.php
6738index f1967de20f1..28678702132 100644
6739--- a/vendor/magento/module-catalog-staging/Setup/UpgradeSchema.php
6740+++ b/vendor/magento/module-catalog-staging/Setup/UpgradeSchema.php
6741@@ -26,11 +26,26 @@ class UpgradeSchema implements UpgradeSchemaInterface
6742 */
6743 public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
6744 {
6745- $this->addIndexIfNotExist(
6746- $setup,
6747- 'catalog_product_entity',
6748- ['entity_id', 'created_in', 'updated_in']
6749- );
6750+ $setup->startSetup();
6751+ if (version_compare($context->getVersion(), '2.1.1', '<')) {
6752+ $this->addIndexIfNotExist(
6753+ $setup,
6754+ 'catalog_product_entity',
6755+ ['entity_id', 'created_in', 'updated_in']
6756+ );
6757+ }
6758+
6759+ if (version_compare($context->getVersion(), '2.1.1.1.2', '<')) {
6760+ $setup->getConnection()->dropForeignKey(
6761+ $setup->getTable('catalog_product_index_price'),
6762+ $setup->getFkName(
6763+ 'catalog_product_index_price',
6764+ 'entity_id',
6765+ 'sequence_product',
6766+ 'sequence_value'
6767+ )
6768+ );
6769+ }
6770 }
6771
6772 /**
6773diff --git a/vendor/magento/module-catalog-staging/etc/module.xml b/vendor/magento/module-catalog-staging/etc/module.xml
6774index d3bb700ced4..ad3f51121c1 100644
6775--- a/vendor/magento/module-catalog-staging/etc/module.xml
6776+++ b/vendor/magento/module-catalog-staging/etc/module.xml
6777@@ -6,7 +6,7 @@
6778 */
6779 -->
6780 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
6781- <module name="Magento_CatalogStaging" setup_version="2.1.1">
6782+ <module name="Magento_CatalogStaging" setup_version="2.1.1.1.2">
6783 <sequence>
6784 <module name="Magento_Catalog"/>
6785 <module name="Magento_CatalogInventory"/>
6786diff --git a/vendor/magento/module-elasticsearch/Model/ResourceModel/Index.php b/vendor/magento/module-elasticsearch/Model/ResourceModel/Index.php
6787index f384260ad84..869b9e89f02 100644
6788--- a/vendor/magento/module-elasticsearch/Model/ResourceModel/Index.php
6789+++ b/vendor/magento/module-elasticsearch/Model/ResourceModel/Index.php
6790@@ -42,6 +42,7 @@ class Index extends \Magento\AdvancedSearch\Model\ResourceModel\Index
6791 * @param CategoryRepositoryInterface $categoryRepository
6792 * @param Config $eavConfig
6793 * @param null $connectionName
6794+ * @SuppressWarnings(Magento.TypeDuplication)
6795 */
6796 public function __construct(
6797 Context $context,
6798@@ -55,7 +56,12 @@ class Index extends \Magento\AdvancedSearch\Model\ResourceModel\Index
6799 $this->productRepository = $productRepository;
6800 $this->categoryRepository = $categoryRepository;
6801 $this->eavConfig = $eavConfig;
6802- parent::__construct($context, $storeManager, $metadataPool, $connectionName);
6803+ parent::__construct(
6804+ $context,
6805+ $storeManager,
6806+ $metadataPool,
6807+ $connectionName
6808+ );
6809 }
6810
6811 /**
6812diff --git a/vendor/magento/module-gift-card/etc/di.xml b/vendor/magento/module-gift-card/etc/di.xml
6813index 53233e3ab39..2de0249cded 100644
6814--- a/vendor/magento/module-gift-card/etc/di.xml
6815+++ b/vendor/magento/module-gift-card/etc/di.xml
6816@@ -149,4 +149,10 @@
6817 </argument>
6818 </arguments>
6819 </type>
6820+ <type name="Magento\GiftCard\Model\ResourceModel\Indexer\Price">
6821+ <arguments>
6822+ <argument name="tableStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\TemporaryTableStrategy</argument>
6823+ <argument name="connectionName" xsi:type="string">indexer</argument>
6824+ </arguments>
6825+ </type>
6826 </config>