diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php index fc0654c8510..8553a65ebfb 100644 --- a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php @@ -126,8 +126,11 @@ public function getBulksByUser($userId) OperationInterface::STATUS_TYPE_COMPLETE ]; $select = $collection->getSelect(); - $select->columns(['status' => $this->calculatedStatusSql->get($operationTableName)]) - ->order(new \Zend_Db_Expr('FIELD(status, ' . implode(',', $statusesArray) . ')')); + $statusExpr = $this->calculatedStatusSql->get($operationTableName); + $select->columns(['status' => $statusExpr]) + ->order( + $this->resourceConnection->getConnection()->getFieldSql((string) $statusExpr, $statusesArray) + ); $collection->addFieldToFilter('user_id', $userId) ->addOrder('start_time'); diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php index 1b011758434..de2539ec38f 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkStatusTest.php @@ -284,6 +284,10 @@ public function testGetBulksByUser(): void $bulkCollection->expects($this->once())->method('getSelect')->willReturn($selectMock); $selectMock->expects($this->once())->method('columns')->willReturnSelf(); $selectMock->expects($this->once())->method('order')->willReturnSelf(); + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock->expects($this->once())->method('getFieldSql')->willReturn('FIELD(status, 1, 2)'); $this->bulkCollectionFactory->expects($this->once())->method('create')->willReturn($bulkCollection); $bulkCollection->expects($this->once())->method('addFieldToFilter')->with('user_id', $userId)->willReturnSelf(); $bulkCollection->expects($this->once())->method('getItems')->willReturn([$this->bulkMock]); diff --git a/app/code/Magento/Backup/Model/ResourceModel/Helper.php b/app/code/Magento/Backup/Model/ResourceModel/Helper.php index 9e217056765..f00028d548b 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/Helper.php +++ b/app/code/Magento/Backup/Model/ResourceModel/Helper.php @@ -178,7 +178,7 @@ public function getHeader() { $dbConfig = $this->getConnection()->getConfig(); - $versionRow = $this->getConnection()->fetchRow('SHOW VARIABLES LIKE \'version\''); + $versionRow = ['Value' => $this->getConnection()->getServerVersion()]; $hostName = !empty($dbConfig['unix_socket']) ? $dbConfig['unix_socket'] : (!empty($dbConfig['host']) ? $dbConfig['host'] : 'localhost'); diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index ebd0d73a2c4..93c35ee43e5 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -485,7 +485,7 @@ private function calculateBundleOptionPrice($priceTable, $dimensions) [ 'min_price' => new \Zend_Db_Expr('MIN(' . $minPrice . ')'), 'alt_price' => new \Zend_Db_Expr('MIN(price)'), - 'max_price' => $connection->getCheckSql('group_type = 0', 'MAX(price)', 'SUM(price)'), + 'max_price' => $connection->getCheckSql('MIN(group_type) = 0', 'MAX(price)', 'SUM(price)'), 'tier_price' => new \Zend_Db_Expr('MIN(' . $tierPrice . ')'), 'alt_tier_price' => new \Zend_Db_Expr('MIN(tier_price)'), ] @@ -734,33 +734,24 @@ private function calculateDynamicBundleSelectionPrice(array $dimensions): void ] ); $select = $this->stockStatusQueryProcessor->execute($select); - $query = str_replace('AS `idx`', 'AS `idx` USE INDEX (PRIMARY)', (string) $select); - - $insertColumns = [ - 'entity_id', - 'customer_group_id', - 'website_id', - 'option_id', - 'selection_id', - 'group_type', - 'is_required', - 'price', - 'tier_price' - ]; - $insertColumns = array_map(function ($item) use ($connection) { - return $connection->quoteIdentifier($item); - }, $insertColumns); - $updateValues = []; - foreach ($insertColumns as $column) { - $updateValues[] = sprintf("%s = VALUES(%s)", $column, $column); - } - - $connection->query(sprintf( - "INSERT INTO `" . $this->getBundleSelectionTable() . "` (%s) %s ON DUPLICATE KEY UPDATE %s", - implode(",", $insertColumns), - $query, - implode(",", $updateValues) - )); + $connection->query( + $connection->insertFromSelect( + $select, + $this->getBundleSelectionTable(), + [ + 'entity_id', + 'customer_group_id', + 'website_id', + 'option_id', + 'selection_id', + 'group_type', + 'is_required', + 'price', + 'tier_price', + ], + \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + ) + ); } /** diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php b/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php index dc12560003a..b0b3fef3c4d 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option/AreBundleOptionsSalable.php @@ -98,7 +98,8 @@ public function execute(int $entityId, int $storeId): bool ); $isOptionSalableExpr = new \Zend_Db_Expr( sprintf( - 'MAX(IFNULL(child_status_store.value, child_status_global.value) != %s)', + 'MAX(CASE WHEN %s != %s THEN 1 ELSE 0 END)', + $connection->getIfNullSql('child_status_store.value', 'child_status_global.value'), ProductStatus::STATUS_DISABLED ) ); diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php index 543437b4b3b..1ac00f28846 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php @@ -324,7 +324,9 @@ public function addPriceFilter($product, $searchMin, $useRegularPrice = false) $minimalPriceExpression = self::INDEX_TABLE_ALIAS . '.price'; } else { $this->getCatalogRuleProcessor()->addPriceData($this, 'selection.product_id'); - $minimalPriceExpression = 'LEAST(minimal_price, IFNULL(catalog_rule_price, minimal_price))'; + $minimalPriceExpression = 'LEAST(minimal_price, ' + . $this->getConnection()->getIfNullSql('catalog_rule_price', 'minimal_price') + . ')'; } $orderByValue = new \Zend_Db_Expr( '(' . diff --git a/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php index 9af291cf624..069497e8b45 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php @@ -154,40 +154,6 @@ public function testCalculateDynamicBundleSelectionPrice(): void `is_required` = VALUES(`is_required`), `price` = VALUES(`price`), `tier_price` = VALUES(`tier_price`)"; - $processedQuery = "INSERT INTO `catalog_product_index_price_bundle_sel_temp` (,,,,,,,,) SELECT `i`.`entity_id`, - `i`.`customer_group_id`, - `i`.`website_id`, - `bo`.`option_id`, - `bs`.`selection_id`, - IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`, - `bo`.`required` AS `is_required`, - LEAST(IF(i.special_price > 0 AND i.special_price < 100, - ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty), - IFNULL((IF(i.tier_percent IS NOT NULL, - ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price * - bs.selection_qty)) AS `price`, - IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), - NULL) AS `tier_price` - FROM `catalog_product_index_price_bundle_temp` AS `i` - INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND - (parent_product.created_in <= 1 AND parent_product.updated_in > 1) - INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id - INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id - INNER JOIN `catalog_product_index_price_replica` AS `idx` USE INDEX (PRIMARY) - ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND - i.website_id = idx.website_id - INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id - WHERE (i.price_type = 0) - AND (si.stock_status = 1) - ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`), - `customer_group_id` = VALUES(`customer_group_id`), - `website_id` = VALUES(`website_id`), - `option_id` = VALUES(`option_id`), - `selection_id` = VALUES(`selection_id`), - `group_type` = VALUES(`group_type`), - `is_required` = VALUES(`is_required`), - `price` = VALUES(`price`), - `tier_price` = VALUES(`tier_price`) ON DUPLICATE KEY UPDATE = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES()"; //@codingStandardsIgnoreEnd $this->connectionMock->expects($this->exactly(3)) ->method('getCheckSql') @@ -232,8 +198,17 @@ public function testCalculateDynamicBundleSelectionPrice(): void $this->connectionMock->expects($this->once())->method('getIfNullSql'); $this->connectionMock->expects($this->once())->method('getLeastSql'); $this->connectionMock->method('select')->willReturn($select); - $this->connectionMock->expects($this->exactly(9))->method('quoteIdentifier'); - $this->connectionMock->expects($this->once())->method('query')->with($processedQuery); + $insertSql = 'INSERT INTO catalog_product_index_price_bundle_sel_temp ... ON DUPLICATE KEY UPDATE'; + $this->connectionMock->expects($this->once()) + ->method('insertFromSelect') + ->with( + $select, + $this->isType('string'), + $this->isType('array'), + AdapterInterface::INSERT_ON_DUPLICATE + ) + ->willReturn($insertSql); + $this->connectionMock->expects($this->once())->method('query')->with($insertSql); $pool = $this->createMock(EntityMetadataInterface::class); $pool->expects($this->once())->method('getLinkField')->willReturn($entity); diff --git a/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php b/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php index 97eb89718a6..e468dbc4456 100644 --- a/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php +++ b/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php @@ -146,7 +146,10 @@ private function initAttributeValues($entityType, $entity, $storeId) $selects = []; foreach ($attributeTables as $attributeTable => $attributeCodes) { $select = $metadata->getEntityConnection()->select() - ->from(['t' => $attributeTable], ['value' => 't.value', 'store_id' => 't.store_id']) + ->from( + ['t' => $attributeTable], + ['value' => $metadata->getEntityConnection()->castToText('t.value'), 'store_id' => 't.store_id'] + ) ->join( ['a' => $this->resourceConnection->getTableName('eav_attribute')], 'a.attribute_id = t.attribute_id', diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index c05aa9e443b..7c40f30fc1e 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -388,7 +388,7 @@ protected function getNonAnchorCategoriesSelect(Store $store) [ 'category_id' => 'cc.entity_id', 'product_id' => 'ccp.product_id', - 'position' => 'ccp.position', + 'position' => new \Zend_Db_Expr('MIN(ccp.position)'), 'is_parent' => new \Zend_Db_Expr('1'), 'store_id' => new \Zend_Db_Expr($store->getId()), 'visibility' => new \Zend_Db_Expr( @@ -640,7 +640,7 @@ protected function createAnchorSelect(Store $store) 'category_id' => 'cc.entity_id', 'product_id' => 'ccp.product_id', 'position' => new \Zend_Db_Expr( - $this->connection->getIfNullSql('ccp2.position', 'MIN(ccp.position) + 10000') + $this->connection->getIfNullSql('MAX(ccp2.position)', 'MIN(ccp.position) + 10000') ), 'is_parent' => new \Zend_Db_Expr('0'), 'store_id' => new \Zend_Db_Expr($store->getId()), @@ -874,16 +874,16 @@ protected function getAllProducts(Store $store) $this->connection->getIfNullSql('cpvs.value', 'cpvd.value') . ' IN (?)', $this->visibility->getVisibleInSiteIds() )->group( - 'cp.entity_id' + ['cp.entity_id', new \Zend_Db_Expr($this->connection->getIfNullSql('cpvs.value', 'cpvd.value'))] )->columns( [ 'category_id' => new \Zend_Db_Expr($store->getRootCategoryId()), 'product_id' => 'cp.entity_id', 'position' => new \Zend_Db_Expr( - $this->connection->getCheckSql('ccp.product_id IS NOT NULL', 'MIN(ccp.position)', '10000') + $this->connection->getCheckSql('COUNT(ccp.product_id) > 0', 'MIN(ccp.position)', '10000') ), 'is_parent' => new \Zend_Db_Expr( - $this->connection->getCheckSql('ccp.product_id IS NOT NULL', '1', '0') + $this->connection->getCheckSql('COUNT(ccp.product_id) > 0', '1', '0') ), 'store_id' => new \Zend_Db_Expr($store->getId()), 'visibility' => new \Zend_Db_Expr( diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php index 6bc30564c87..f7ec0bd476e 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Eraser.php @@ -109,7 +109,10 @@ public function removeDisabledProducts(array &$ids, $storeId) . 'product_table.' . $metadata->getLinkField(), [] ); - $select->where('IFNULL(status_attr.value, status_global_attr.value) = ?', Status::STATUS_DISABLED); + $select->where( + $this->connection->getIfNullSql('status_attr.value', 'status_global_attr.value') . ' = ?', + Status::STATUS_DISABLED + ); $result = $this->connection->query($select); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index d7dcf295a7d..b4cae61e9ed 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -723,9 +723,7 @@ private function getProductsCountQuery(array $categoryIds, $addVisibilityFilter if (true === $addVisibilityFilter) { $select->where('cat_index.visibility in (?)', $this->catalogProductVisibility->getVisibleInSiteIds()); } - if (count($categoryIds) > 1) { - $select->group('cat_index.category_id'); - } + $select->group('cat_index.category_id'); return $select; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php b/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php index da4f5344bb6..c6ed51812db 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php @@ -239,10 +239,18 @@ protected function _addLoadAttributesSelectValues($select, $table, $type) $storeId = $this->getStoreId(); if ($storeId) { $connection = $this->getConnection(); - $valueExpr = $connection->getCheckSql('t_s.value_id IS NULL', 't_d.value', 't_s.value'); + $valueExpr = $connection->getCheckSql( + 't_s.value_id IS NULL', + $connection->castToText('t_d.value'), + $connection->castToText('t_s.value') + ); $select->columns( - ['default_value' => 't_d.value', 'store_value' => 't_s.value', 'value' => $valueExpr] + [ + 'default_value' => $connection->castToText('t_d.value'), + 'store_value' => $connection->castToText('t_s.value'), + 'value' => $valueExpr, + ] ); } else { $select = parent::_addLoadAttributesSelectValues($select, $table, $type); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/JoinMinimalPosition.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/JoinMinimalPosition.php index 132f0847b64..39f189b62a1 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/JoinMinimalPosition.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection/JoinMinimalPosition.php @@ -70,7 +70,8 @@ public function execute(Collection $collection, array $categoryIds): void [] ); } - $positions[] = $connection->getIfNullSql($table . '.position', '~0'); + // Portable max bigint for missing position. + $positions[] = $connection->getIfNullSql($table . '.position', '9223372036854775807'); } // Ensures that position attribute is registered in _joinFields diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index 1e53e378869..0af38a583cf 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -224,9 +224,9 @@ public function createBatchBaseSelect($storeId, $attributeId) ), [] )->columns([ - 'label' => $this->getConnection()->getIfNullSql('`value`.`label`', '`default_value`.`label`'), - 'position' => $this->getConnection()->getIfNullSql('`value`.`position`', '`default_value`.`position`'), - 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'), + 'label' => $this->getConnection()->getIfNullSql('value.label', 'default_value.label'), + 'position' => $this->getConnection()->getIfNullSql('value.position', 'default_value.position'), + 'disabled' => $this->getConnection()->getIfNullSql('value.disabled', 'default_value.disabled'), 'label_default' => 'default_value.label', 'position_default' => 'default_value.position', 'disabled_default' => 'default_value.disabled' @@ -236,7 +236,7 @@ public function createBatchBaseSelect($storeId, $attributeId) )->where( $mainTableAlias . '.disabled = 0' )->order( - $positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC + new \Zend_Db_Expr($positionCheckSql . ' ' . \Magento\Framework\DB\Select::SQL_ASC) ); return $select; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index c15b366db2f..82ebef48654 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -189,7 +189,7 @@ protected function _prepareRelationIndexSelect(?array $parentIds = null) 'i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id', [] )->group( - ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] + ['e.entity_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] )->columns( [ 'parent_id' => 'e.entity_id', diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 46870ca3279..bb81eb1a994 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -454,7 +454,8 @@ protected function getSelect($entityIds = null, $type = null) ); $currentDate = 'cwd.website_date'; - $maxUnsignedBigint = '~0'; + // Portable max bigint (not MySQL ~0). + $maxUnsignedBigint = '9223372036854775807'; $specialFromDate = $connection->getDatePartSql($specialFrom); $specialToDate = $connection->getDatePartSql($specialTo); $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; @@ -838,7 +839,7 @@ protected function hasEntity() */ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) { - $maxUnsignedBigint = '~0'; + $maxUnsignedBigint = '9223372036854775807'; return $this->getConnection()->getCheckSql( implode( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index 7a21fb65e0e..69430a8973b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -144,7 +144,7 @@ public function getQuery(array $dimensions, string $productType, array $entityId $specialTo = $this->joinAttributeProcessor->process($select, 'special_to_date'); $currentDate = 'cwd.website_date'; - $maxUnsignedBigint = '~0'; + $maxUnsignedBigint = '9223372036854775807'; $specialFromDate = $connection->getDatePartSql($specialFrom); $specialToDate = $connection->getDatePartSql($specialTo); $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; @@ -257,7 +257,7 @@ public function getQuery(array $dimensions, string $productType, array $entityId */ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) { - $maxUnsignedBigint = '~0'; + $maxUnsignedBigint = '9223372036854775807'; return $this->getConnection()->getCheckSql( implode( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/StatusBaseSelectProcessor.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/StatusBaseSelectProcessor.php index d5a1676ac4c..ba926d7a3a4 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/StatusBaseSelectProcessor.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/StatusBaseSelectProcessor.php @@ -76,7 +76,10 @@ public function process(Select $select) [] ); - $select->where('IFNULL(status_attr.value, status_global_attr.value) = ?', Status::STATUS_ENABLED); + $select->where( + $select->getConnection()->getIfNullSql('status_attr.value', 'status_global_attr.value') . ' = ?', + Status::STATUS_ENABLED + ); return $select; } diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php index 09bfe656ae2..3099218dda0 100644 --- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php @@ -41,6 +41,8 @@ public function __construct( } /** + * Merge global-scope catalog attribute values into the current-store snapshot. + * * @param ReadSnapshot $subject * @param array $entityData * @param string $entityType @@ -73,11 +75,12 @@ public function afterExecute(ReadSnapshot $subject, array $entityData, $entityTy if ($globalAttributes) { $selects = []; foreach ($globalAttributes as $table => $attributeIds) { - $select = $connection->select() - ->from( - ['t' => $table], - ['value' => 't.value', 'attribute_id' => 't.attribute_id'] - ) + $select = $connection->select(); + // Align types across UNION ALL branches. + $select->from( + ['t' => $table], + ['value' => $connection->castToText('t.value'), 'attribute_id' => 't.attribute_id'] + ) ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]) ->where('attribute_id' . ' in (?)', $attributeIds) ->where('store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php index 25c8f77ca73..22bccacf49a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php @@ -386,19 +386,19 @@ function ($arg) use ($storeId) { $this->connection->expects($this->any())->method('getIfNullSql')->willReturnMap( [ [ - '`value`.`label`', - '`default_value`.`label`', - 'IFNULL(`value`.`label`, `default_value`.`label`)' + 'value.label', + 'default_value.label', + 'IFNULL(value.label, default_value.label)' ], [ - '`value`.`position`', - '`default_value`.`position`', - 'IFNULL(`value`.`position`, `default_value`.`position`)' + 'value.position', + 'default_value.position', + 'IFNULL(value.position, default_value.position)' ], [ - '`value`.`disabled`', - '`default_value`.`disabled`', - 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)' + 'value.disabled', + 'default_value.disabled', + 'IFNULL(value.disabled, default_value.disabled)' ] ] ); @@ -432,9 +432,9 @@ function ($arg1, $arg2) use ($attributeId, $productId) { ->method('columns') ->with( [ - 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)', - 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)', - 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)', + 'label' => 'IFNULL(value.label, default_value.label)', + 'position' => 'IFNULL(value.position, default_value.position)', + 'disabled' => 'IFNULL(value.disabled, default_value.disabled)', 'label_default' => 'default_value.label', 'position_default' => 'default_value.position', 'disabled_default' => 'default_value.disabled' @@ -466,7 +466,7 @@ function ( ->with(['entity' => $getTableReturnValue], 'main.value_id = entity.value_id', ['entity_id']) ->willReturn($this->select); $this->select->expects($this->once())->method('order') - ->with($positionCheckSql . ' ' . Select::SQL_ASC) + ->with(new \Zend_Db_Expr($positionCheckSql . ' ' . Select::SQL_ASC)) ->willReturnSelf(); $this->connection->expects($this->once())->method('fetchAll') ->with($this->select) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php index 70c0fa77e7c..044bf3d00dd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/StatusBaseSelectProcessorTest.php @@ -65,6 +65,12 @@ protected function setUp(): void $this->storeManager = $this->createMock(StoreManagerInterface::class); $this->select = $this->createMock(Select::class); + $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $connection->method('getIfNullSql') + ->with('status_attr.value', 'status_global_attr.value') + ->willReturn('IFNULL(status_attr.value, status_global_attr.value)'); + $this->select->method('getConnection')->willReturn($connection); + $this->statusBaseSelectProcessor = (new ObjectManager($this))->getObject(StatusBaseSelectProcessor::class, [ 'eavConfig' => $this->eavConfig, 'metadataPool' => $this->metadataPool, diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index 54cbef64c4f..7277935bc8e 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -127,7 +127,8 @@ protected function applySorting() /** @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection $collection */ $collection = $this->getContext()->getDataProvider()->getCollection(); - $select = $collection->getConnection()->select(); + $connection = $collection->getConnection(); + $select = $connection->select(); $select->from( ['cpw' => $collection->getTable('catalog_product_website')], ['product_id'] @@ -135,9 +136,7 @@ protected function applySorting() ['sw' => $collection->getTable('store_website')], 'cpw.website_id = sw.website_id', [ - $this->websiteNames => new \Zend_Db_Expr( - 'GROUP_CONCAT(sw.name ORDER BY sw.website_id ASC SEPARATOR \',\')' - ) + $this->websiteNames => $connection->getGroupConcatSql('sw.name', ',', 'sw.website_id ASC') ] )->group( 'cpw.product_id' diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexerTableSwapper.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexerTableSwapper.php index 79e72060032..62ff4bb78d1 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexerTableSwapper.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexerTableSwapper.php @@ -47,12 +47,9 @@ private function createTemporaryTable(string $originalTableName): string $originalTableName . '__temp' . $this->generateRandomSuffix() ); - $this->resourceConnection->getConnection()->query( - sprintf( - 'create table %s like %s', - $temporaryTableName, - $this->resourceConnection->getTableName($originalTableName) - ) + $this->resourceConnection->getConnection()->createTableLike( + $temporaryTableName, + $this->resourceConnection->getTableName($originalTableName) ); return $temporaryTableName; diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index bdeaba2bae0..f4480debbea 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -336,7 +336,7 @@ private function unifyField($field, $backendType = 'varchar') if ($backendType == 'datetime') { $expr = $this->connection->getDateFormatSql($field, '%Y-%m-%d %H:%i:%s'); } else { - $expr = $field; + $expr = $this->connection->castToText($field); } return $expr; diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index febe0e1a5e3..34adfa16ae3 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -54,10 +54,9 @@ public function apply() $ids[] = (int)$item->getId(); } - $orderList = implode(',', $ids); - $this->collection->getSelect() - ->where('e.entity_id IN (?)', $ids) + $select = $this->collection->getSelect(); + $select->where('e.entity_id IN (?)', $ids) ->reset(\Magento\Framework\DB\Select::ORDER) - ->order(new \Magento\Framework\DB\Sql\Expression("FIELD(e.entity_id, $orderList)")); + ->order($select->getAdapter()->getFieldSql('e.entity_id', $ids)); } } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php index 7b36443f6cd..9d5c413b896 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php @@ -160,8 +160,17 @@ private function fetch(ContextInterface $context, array $attributeCodes) : array $childCollection->addWebsiteFilter($context->getExtensionAttributes()->getStore()->getWebsiteId()); $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $childCollection->getSelect()->group('e.' . $linkField); - $childCollection->getSelect()->columns([ - 'parent_ids' => new \Zend_Db_Expr('GROUP_CONCAT(link_table.parent_id)') + $select = $childCollection->getSelect(); + // Drop ungrouped parent_id; parent_ids is aggregated instead. + $select->setPart( + \Magento\Framework\DB\Select::COLUMNS, + array_values(array_filter( + $select->getPart(\Magento\Framework\DB\Select::COLUMNS), + static fn(array $column) => !($column[0] === 'link_table' && $column[1] === 'parent_id') + )) + ); + $select->columns([ + 'parent_ids' => $select->getAdapter()->getGroupConcatSql('link_table.parent_id') ]); $attributeCodes = array_unique(array_merge($this->attributeCodes, $attributeCodes)); diff --git a/app/code/Magento/Customer/Setup/Patch/Data/AddSecurityTrackingAttributes.php b/app/code/Magento/Customer/Setup/Patch/Data/AddSecurityTrackingAttributes.php index 14f36423ed6..dc9c4e2d78c 100644 --- a/app/code/Magento/Customer/Setup/Patch/Data/AddSecurityTrackingAttributes.php +++ b/app/code/Magento/Customer/Setup/Patch/Data/AddSecurityTrackingAttributes.php @@ -91,7 +91,7 @@ public function apply() $this->moduleDataSetup->getConnection()->update( $configTable, - ['value' => new \Zend_Db_Expr('value*24')], + ['value' => new \Zend_Db_Expr($this->moduleDataSetup->getConnection()->castToNumeric('value') . '*24')], ['path = ?' => Customer::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD] ); diff --git a/app/code/Magento/Customer/Setup/Patch/Data/SessionIDColumnCleanUp.php b/app/code/Magento/Customer/Setup/Patch/Data/SessionIDColumnCleanUp.php index 4871c078637..c3e5bf2af32 100644 --- a/app/code/Magento/Customer/Setup/Patch/Data/SessionIDColumnCleanUp.php +++ b/app/code/Magento/Customer/Setup/Patch/Data/SessionIDColumnCleanUp.php @@ -66,9 +66,8 @@ public function apply() private function cleanCustomerVisitorTable() { $tableName = $this->moduleDataSetup->getTable('customer_visitor'); - // phpcs:ignore Magento2.SQL.RawQuery $rawQuery = sprintf( - 'UPDATE %s SET session_id = NULL WHERE session_id IS NOT NULL LIMIT 1000', + 'UPDATE %s SET session_id = NULL WHERE session_id IS NOT NULL', $tableName ); diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index f34f675831f..6a46435b091 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -1063,7 +1063,11 @@ protected function _loadModelAttributes($object) $attribute = current($this->_attributesByTable[$table]); $eavType = $attribute->getBackendType(); $select = $this->_getLoadAttributesSelect($object, $table); - $selects[$eavType][] = $select->columns('*'); + $selects[$eavType][] = $select->columns([ + 'attribute_id' => 'attribute_id', + 'value_id' => 'value_id', + 'value' => $this->getConnection()->castToText($this->getConnection()->quoteIdentifier('value')), + ]); } $selectGroups = $this->_resourceHelper->getLoadAttributesSelectGroups($selects); foreach ($selectGroups as $selects) { diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index e2f40374ac1..ca6b668db49 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -76,15 +76,20 @@ public function getAllOptions($withEmpty = true, $defaultValues = false) } $attributeId = $this->getAttribute()->getId(); if (!isset($this->_options[$storeId][$attributeId])) { - $collection = $this->_attrOptionCollectionFactory->create()->setPositionOrder( - 'asc' - )->setAttributeFilter( - $attributeId - )->setStoreFilter( - $storeId - )->load(); - $this->_options[$storeId][$attributeId] = $collection->toOptionArray(); - $this->_optionsDefault[$storeId][$attributeId] = $collection->toOptionArray('default_value'); + if (!$attributeId) { + $this->_options[$storeId][$attributeId] = []; + $this->_optionsDefault[$storeId][$attributeId] = []; + } else { + $collection = $this->_attrOptionCollectionFactory->create()->setPositionOrder( + 'asc' + )->setAttributeFilter( + $attributeId + )->setStoreFilter( + $storeId + )->load(); + $this->_options[$storeId][$attributeId] = $collection->toOptionArray(); + $this->_optionsDefault[$storeId][$attributeId] = $collection->toOptionArray('default_value'); + } } $options = $defaultValues ? $this->_optionsDefault[$storeId][$attributeId] diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index 737a9f07c61..743137c4c61 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -1315,7 +1315,7 @@ protected function _getLoadAttributesSelect($table, $attributeIds = []) */ protected function _addLoadAttributesSelectValues($select, $table, $type) { - $select->columns(['value' => 't_d.value']); + $select->columns(['value' => $this->getConnection()->castToText('t_d.value')]); return $select; } diff --git a/app/code/Magento/Eav/Model/Mview/ChangelogBatchWalker/IdsSelectBuilder.php b/app/code/Magento/Eav/Model/Mview/ChangelogBatchWalker/IdsSelectBuilder.php index 44aad78042c..3fbbaaf80ba 100644 --- a/app/code/Magento/Eav/Model/Mview/ChangelogBatchWalker/IdsSelectBuilder.php +++ b/app/code/Magento/Eav/Model/Mview/ChangelogBatchWalker/IdsSelectBuilder.php @@ -43,16 +43,13 @@ public function __construct( */ public function build(ChangelogInterface $changelog): Select { - $numberOfAttributes = $this->calculateEavAttributeSize($changelog); - $this->setGroupConcatMax($numberOfAttributes); - $changelogTableName = $this->resourceConnection->getTableName($changelog->getName()); $connection = $this->resourceConnection->getConnection(); $columns = [ $changelog->getColumnName(), - 'attribute_ids' => new Expression('GROUP_CONCAT(attribute_id)'), + 'attribute_ids' => $connection->getGroupConcatSql('attribute_id'), 'store_id' ]; diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php index 3f108ea2e96..4f8c1dde3a7 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Collection.php @@ -300,7 +300,11 @@ public function setAttributeGroupFilter($groupId) */ public function addAttributeGrouping() { - $this->getSelect()->group('main_table.attribute_id'); + $select = $this->getSelect(); + $select->group('main_table.attribute_id'); + if (array_key_exists('additional_table', $select->getPart(Select::FROM))) { + $select->group('additional_table.attribute_id'); + } return $this; } @@ -346,12 +350,11 @@ public function addHasOptionsFilter() $this->getSelect()->joinLeft( ['ao' => $this->getTable('eav_attribute_option')], 'ao.attribute_id = main_table.attribute_id', - 'option_id' - )->group( - 'main_table.attribute_id' + [] )->where( $orWhere ); + $this->addAttributeGrouping(); return $this; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php index 898efad0e3e..38e8ec6b25d 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php @@ -156,7 +156,7 @@ public function execute($entityType, $entityData, $arguments = []) $select = $connection->select() ->from( ['t' => $attributeTable], - ['value' => 't.value', 'attribute_id' => 't.attribute_id'] + ['value' => $connection->castToText('t.value'), 'attribute_id' => 't.attribute_id'] ) ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]) ->where('attribute_id IN (?)', $attributeIds, \Zend_Db::INT_TYPE); diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index ba7342c554b..acd19d2aaa5 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -70,11 +70,10 @@ public function apply() foreach ($items as $item) { $ids[] = (int)$item->getId(); } - $orderList = implode(',', $ids); $this->collection->getSelect() ->where('e.entity_id IN (?)', $ids) ->reset(\Magento\Framework\DB\Select::ORDER) - ->order(new \Zend_Db_Expr("FIELD(e.entity_id,$orderList)")); + ->order($this->collection->getSelect()->getAdapter()->getFieldSql('e.entity_id', $ids)); } /** diff --git a/app/code/Magento/ImportExport/Model/ResourceModel/Import/Data.php b/app/code/Magento/ImportExport/Model/ResourceModel/Import/Data.php index 75a708bd90c..6b15936ae92 100644 --- a/app/code/Magento/ImportExport/Model/ResourceModel/Import/Data.php +++ b/app/code/Magento/ImportExport/Model/ResourceModel/Import/Data.php @@ -123,9 +123,12 @@ public function cleanBunches() */ public function cleanProcessedBunches() { - $this->getConnection()->delete( + $connection = $this->getConnection(); + $connection->delete( $this->getMainTable(), - 'is_processed = 1 OR TIMESTAMPADD(DAY, 1, updated_at) < CURRENT_TIMESTAMP() ' + 'is_processed = 1 OR ' + . $connection->getDateAddSql('updated_at', 1, \Magento\Framework\DB\Adapter\AdapterInterface::INTERVAL_DAY) + . ' < CURRENT_TIMESTAMP' ); } diff --git a/app/code/Magento/Integration/Model/ResourceModel/Oauth/Consumer.php b/app/code/Magento/Integration/Model/ResourceModel/Oauth/Consumer.php index 3af513e40c9..8b72a9805a3 100644 --- a/app/code/Magento/Integration/Model/ResourceModel/Oauth/Consumer.php +++ b/app/code/Magento/Integration/Model/ResourceModel/Oauth/Consumer.php @@ -68,7 +68,7 @@ public function getTimeInSecondsSinceCreation($consumerId) $select = $connection->select() ->from($this->getMainTable()) ->reset(\Magento\Framework\DB\Select::COLUMNS) - ->columns(new \Zend_Db_Expr('CURRENT_TIMESTAMP() - created_at')) + ->columns(new \Zend_Db_Expr('CURRENT_TIMESTAMP - created_at')) ->where('entity_id = ?', $consumerId); return $connection->fetchOne($select); diff --git a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php index 03619728a6a..9342a1dbef5 100644 --- a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php +++ b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php @@ -45,7 +45,7 @@ public function apply(): void $select = $connection->select() ->from($tableName, ['role_id']) - ->where('resource_id = "Magento_Cms::media_gallery"'); + ->where("resource_id = 'Magento_Cms::media_gallery'"); $insertData = $this->getInsertData($connection->fetchCol($select)); diff --git a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php index 497f359fafa..abd7ffa7ec1 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php @@ -81,15 +81,15 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec [] )->columns([ 'video_provider' => $originalResourceModel->getConnection() - ->getIfNullSql('`value_video`.`provider`', '`default_value_video`.`provider`'), + ->getIfNullSql('value_video.provider', 'default_value_video.provider'), 'video_url' => $originalResourceModel->getConnection() - ->getIfNullSql('`value_video`.`url`', '`default_value_video`.`url`'), + ->getIfNullSql('value_video.url', 'default_value_video.url'), 'video_title' => $originalResourceModel->getConnection() - ->getIfNullSql('`value_video`.`title`', '`default_value_video`.`title`'), + ->getIfNullSql('value_video.title', 'default_value_video.title'), 'video_description' => $originalResourceModel->getConnection() - ->getIfNullSql('`value_video`.`description`', '`default_value_video`.`description`'), + ->getIfNullSql('value_video.description', 'default_value_video.description'), 'video_metadata' => $originalResourceModel->getConnection() - ->getIfNullSql('`value_video`.`metadata`', '`default_value_video`.`metadata`'), + ->getIfNullSql('value_video.metadata', 'default_value_video.metadata'), 'video_provider_default' => 'default_value_video.provider', 'video_url_default' => 'default_value_video.url', 'video_title_default' => 'default_value_video.title', diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php index 6390398dd0e..c1abb6fa468 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php @@ -106,8 +106,8 @@ public function appendSummaryFieldsToCollection( ['review_summary' => $this->getMainTable()], $joinCond, [ - 'reviews_count' => new \Zend_Db_Expr("IFNULL(review_summary.reviews_count, 0)"), - 'rating_summary' => new \Zend_Db_Expr("IFNULL(review_summary.rating_summary, 0)") + 'reviews_count' => $this->getConnection()->getIfNullSql('review_summary.reviews_count', 0), + 'rating_summary' => $this->getConnection()->getIfNullSql('review_summary.rating_summary', 0) ] ); } diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index a45d955dae9..bc0e66d1c29 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -282,13 +282,19 @@ private function buildConditions(AbstractCollection $collection, Combine $combin } if (!empty($conditions) && !empty($attributeField)) { - $conditions = $this->_connection->quote( - array_map('trim', explode(',', $conditions)) + $conditionValues = explode( + ', ', + $this->_connection->quote(array_map('trim', explode(',', $conditions))) ); $collection->getSelect()->reset(Select::ORDER); $collection->getSelect()->order( $this->_expressionFactory->create( - ['expression' => "FIELD($attributeField, $conditions)"] + [ + 'expression' => (string) $this->_connection->getFieldSql( + $attributeField, + $conditionValues + ) + ] ) ); } diff --git a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php index e23e6fb6904..db01238c4bb 100644 --- a/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php +++ b/app/code/Magento/Security/Model/ResourceModel/UserExpiration/Collection.php @@ -40,12 +40,12 @@ public function addActiveExpiredUsersFilter(): Collection $currentTime = new \DateTime(); $currentTime->format('Y-m-d H:i:s'); $this->getSelect()->joinLeft( - ['user' => $this->getTable('admin_user')], - 'main_table.user_id = user.user_id', + ['admin_user' => $this->getTable('admin_user')], + 'main_table.user_id = admin_user.user_id', ['is_active'] ); $this->addFieldToFilter('expires_at', ['lt' => $currentTime]) - ->addFieldToFilter('user.is_active', 1); + ->addFieldToFilter('admin_user.is_active', 1); return $this; } diff --git a/app/code/Magento/Security/Setup/Patch/Data/SessionIDColumnCleanUp.php b/app/code/Magento/Security/Setup/Patch/Data/SessionIDColumnCleanUp.php index 8a3124404c9..3d26cc58ae8 100644 --- a/app/code/Magento/Security/Setup/Patch/Data/SessionIDColumnCleanUp.php +++ b/app/code/Magento/Security/Setup/Patch/Data/SessionIDColumnCleanUp.php @@ -66,9 +66,8 @@ public function apply() private function cleanAdminUserSessionTable() { $tableName = $this->moduleDataSetup->getTable('admin_user_session'); - // phpcs:ignore Magento2.SQL.RawQuery $rawQuery = sprintf( - 'UPDATE %s SET session_id = NULL WHERE session_id IS NOT NULL LIMIT 1000', + 'UPDATE %s SET session_id = NULL WHERE session_id IS NOT NULL', $tableName ); diff --git a/app/code/Magento/Vault/Setup/Patch/Data/SetCreditCardAsDefaultTokenType.php b/app/code/Magento/Vault/Setup/Patch/Data/SetCreditCardAsDefaultTokenType.php index 36282d1abe9..888f3f40a94 100644 --- a/app/code/Magento/Vault/Setup/Patch/Data/SetCreditCardAsDefaultTokenType.php +++ b/app/code/Magento/Vault/Setup/Patch/Data/SetCreditCardAsDefaultTokenType.php @@ -47,7 +47,7 @@ public function apply() [ PaymentTokenInterface::TYPE => CreditCardTokenFactory::TOKEN_TYPE_CREDIT_CARD ], - PaymentTokenInterface::TYPE . ' = ""' + PaymentTokenInterface::TYPE . " = ''" ); $this->moduleDataSetup->getConnection()->endSetup(); diff --git a/app/code/Magento/Weee/Plugin/Catalog/ResourceModel/Product/WeeeAttributeProductSort.php b/app/code/Magento/Weee/Plugin/Catalog/ResourceModel/Product/WeeeAttributeProductSort.php index e51f21881a4..db2cbadf543 100644 --- a/app/code/Magento/Weee/Plugin/Catalog/ResourceModel/Product/WeeeAttributeProductSort.php +++ b/app/code/Magento/Weee/Plugin/Catalog/ResourceModel/Product/WeeeAttributeProductSort.php @@ -44,14 +44,17 @@ public function afterBuild( int $productId, int $storeId ):array { - $select = $this->resourceConnection->getConnection()->select(); + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select(); + $weeeValue = $connection->getIfNullSql( + 'weee_child.value', + (string) $connection->getIfNullSql('weee_parent.value', 0) + ); foreach ($result as $select) { $select->columns( [ - 'weee_min_price' => new \Zend_Db_Expr( - '(t.min_price + IFNULL(weee_child.value, IFNULL(weee_parent.value, 0)))' - ) + 'weee_min_price' => new \Zend_Db_Expr('(t.min_price + ' . $weeeValue . ')') ] )->joinLeft( ['weee_child' => $this->resourceConnection->getTableName('weee_tax')], diff --git a/lib/internal/Magento/Framework/Cache/Backend/Database.php b/lib/internal/Magento/Framework/Cache/Backend/Database.php index 053ef66a6e2..9a86149059c 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/Database.php +++ b/lib/internal/Magento/Framework/Cache/Backend/Database.php @@ -230,17 +230,17 @@ public function save($data, $id, $tags = [], $specificLifetime = null) $time = time(); $expire = $lifetime === 0 || $lifetime === null ? 0 : $time + $lifetime; - $idCol = $connection->quoteIdentifier('id'); - $dataCol = $connection->quoteIdentifier('data'); - $createCol = $connection->quoteIdentifier('create_time'); - $updateCol = $connection->quoteIdentifier('update_time'); - $expireCol = $connection->quoteIdentifier('expire_time'); - - $query = "INSERT INTO {$dataTable} ({$idCol}, {$dataCol}, {$createCol}, {$updateCol}, {$expireCol}) " . - "VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE {$dataCol}=VALUES({$dataCol}), " . - "{$updateCol}=VALUES({$updateCol}), {$expireCol}=VALUES({$expireCol})"; - - $result = $connection->query($query, [$id, $data, $time, $time, $expire])->rowCount(); + $result = $connection->insertOnDuplicate( + $dataTable, + [ + 'id' => $id, + 'data' => $data, + 'create_time' => $time, + 'update_time' => $time, + 'expire_time' => $expire, + ], + ['data', 'update_time', 'expire_time'] + ); } if ($result) { $result = $this->_saveTags($id, $tags); diff --git a/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php b/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php index e6f60fb70b4..8c5f2b48d50 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php +++ b/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php @@ -819,6 +819,61 @@ public function getCheckSql($condition, $true, $false); */ public function getIfNullSql($expression, $value = 0); + /** + * Concatenate grouped column values with a separator. + * + * @param string|\Zend_Db_Expr $expression + * @param string $separator + * @param string|\Zend_Db_Expr|null $orderBy + * @param bool $distinct + * @return \Zend_Db_Expr + */ + public function getGroupConcatSql($expression, $separator = ',', $orderBy = null, $distinct = false); + + /** + * Rank an expression by a fixed value list for ORDER BY. + * + * @param string|\Zend_Db_Expr $expression + * @param array $values + * @return \Zend_Db_Expr + */ + public function getFieldSql($expression, array $values); + + /** + * Cast an expression to text for UNION type alignment + * + * @param string|\Zend_Db_Expr $expression + * @return \Zend_Db_Expr + */ + public function castToText($expression); + + /** + * Cast an expression to a numeric type for arithmetic + * + * @param string|\Zend_Db_Expr $expression + * @return \Zend_Db_Expr + */ + public function castToNumeric($expression); + + /** + * CREATE TABLE new LIKE origin + * + * @param string $newTableName + * @param string $originTableName + * @return \Zend_Db_Statement_Interface + */ + public function createTableLike($newTableName, $originTableName); + + /** + * CREATE TEMPORARY TABLE from a SELECT (and optional index definitions) + * + * @param string $name + * @param string[] $indexStatements + * @param \Magento\Framework\DB\Select $select + * @return \Zend_Db_Statement_Interface + */ + public function createTemporaryTableFromSelect($name, array $indexStatements, \Magento\Framework\DB\Select $select); + /** * Generate fragment of SQL, that combine together (concatenate) the results from data array * diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index f41d0ed17f7..050decc7d63 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -3442,6 +3442,79 @@ public function getIfNullSql($expression, $value = 0) return new \Zend_Db_Expr($expression); } + /** + * @inheritdoc + */ + public function getGroupConcatSql($expression, $separator = ',', $orderBy = null, $distinct = false) + { + $sql = 'GROUP_CONCAT(' . ($distinct ? 'DISTINCT ' : '') . $expression; + if ($orderBy !== null) { + $sql .= ' ORDER BY ' . $orderBy; + } + return new \Zend_Db_Expr($sql . ' SEPARATOR ' . $this->quote($separator) . ')'); + } + + /** + * @inheritdoc + */ + public function getFieldSql($expression, array $values) + { + $parts = []; + foreach ($values as $value) { + if ($value === '') { + continue; + } + $parts[] = $value; + } + if (!$parts) { + return new \Zend_Db_Expr('0'); + } + return new \Zend_Db_Expr('FIELD(' . $expression . ', ' . implode(', ', $parts) . ')'); + } + + /** + * @inheritdoc + */ + public function createTableLike($newTableName, $originTableName) + { + return $this->query(sprintf( + 'CREATE TABLE %s LIKE %s', + $this->quoteIdentifier($newTableName), + $this->quoteIdentifier($originTableName) + )); + } + + /** + * @inheritdoc + */ + public function createTemporaryTableFromSelect($name, array $indexStatements, Select $select) + { + $sql = sprintf( + 'CREATE TEMPORARY TABLE %s %s ENGINE=%s IGNORE (%s)', + $this->quoteIdentifier($name), + $indexStatements ? '(' . implode(',', $indexStatements) . ')' : '', + $this->quoteIdentifier('innodb'), + $select + ); + return $this->query($sql, $select->getBind()); + } + + /** + * @inheritdoc + */ + public function castToText($expression) + { + return new \Zend_Db_Expr((string) $expression); + } + + /** + * @inheritdoc + */ + public function castToNumeric($expression) + { + return new \Zend_Db_Expr('CAST(' . $expression . ' AS DECIMAL(20,6))'); + } + /** * Generates case SQL fragment * diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php index b1eacc98bdd..08f01be93d9 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php @@ -174,7 +174,8 @@ private function calculateBatchSize(Select $select) ] ); $row = $this->connection->fetchRow($wrapperSelect); - $this->minValue = $row['max']; + // Empty batch: MAX() is NULL; keep the previous minValue. + $this->minValue = $row['max'] ?? $this->minValue; return (int)$row['cnt']; } diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php index 1ef13dd7424..d2b79e527ae 100644 --- a/lib/internal/Magento/Framework/DB/Select.php +++ b/lib/internal/Magento/Framework/DB/Select.php @@ -57,12 +57,14 @@ class Select extends \Zend_Db_Select * Class constructor * Add straight join support * - * @param Adapter\Pdo\Mysql $adapter + * Accepts any Zend DB adapter, not only Pdo\Mysql. + * + * @param \Zend_Db_Adapter_Abstract $adapter * @param Select\SelectRenderer $selectRenderer * @param array $parts */ public function __construct( - \Magento\Framework\DB\Adapter\Pdo\Mysql $adapter, + \Zend_Db_Adapter_Abstract $adapter, \Magento\Framework\DB\Select\SelectRenderer $selectRenderer, $parts = [] ) { diff --git a/lib/internal/Magento/Framework/DB/TemporaryTableService.php b/lib/internal/Magento/Framework/DB/TemporaryTableService.php index 6ee15d4013f..25026dc2781 100644 --- a/lib/internal/Magento/Framework/DB/TemporaryTableService.php +++ b/lib/internal/Magento/Framework/DB/TemporaryTableService.php @@ -120,18 +120,7 @@ public function createFromSelect( $indexStatements[] = sprintf('%s(%s)', $indexType, $renderedColumns); } - $statement = sprintf( - 'CREATE TEMPORARY TABLE %s %s ENGINE=%s IGNORE (%s)', - $adapter->quoteIdentifier($name), - $indexStatements ? '(' . implode(',', $indexStatements) . ')' : '', - $adapter->quoteIdentifier($dbEngine), - "{$select}" - ); - - $adapter->query( - $statement, - $select->getBind() - ); + $adapter->createTemporaryTableFromSelect($name, $indexStatements, $select); $this->createdTableAdapters[$name] = $adapter; diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php index a5f89a5256c..cdc70e9438a 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php @@ -1071,4 +1071,78 @@ public function testDestruct(): void $adapter->__destruct(); $this->assertEquals(0, $adapter->getTransactionLevel()); } + + public function testGetGroupConcatSql(): void + { + $adapter = $this->getMysqlPdoAdapterMock(['query', 'quote']); + $adapter->method('quote')->willReturnCallback(static function ($value) { + return "'" . $value . "'"; + }); + + $this->assertSame( + "GROUP_CONCAT(sku SEPARATOR ',')", + (string) $adapter->getGroupConcatSql('sku') + ); + $this->assertSame( + "GROUP_CONCAT(DISTINCT sku ORDER BY sku SEPARATOR '|')", + (string) $adapter->getGroupConcatSql('sku', '|', 'sku', true) + ); + } + + public function testGetFieldSql(): void + { + $adapter = $this->getMysqlPdoAdapterMock([]); + $this->assertSame('0', (string) $adapter->getFieldSql('status', [])); + $this->assertSame('0', (string) $adapter->getFieldSql('status', [''])); + $this->assertSame( + 'FIELD(status, 1, 2, 3)', + (string) $adapter->getFieldSql('status', [1, '', 2, 3]) + ); + } + + public function testCastHelpers(): void + { + $adapter = $this->getMysqlPdoAdapterMock([]); + $this->assertSame('value', (string) $adapter->castToText('value')); + $this->assertSame('CAST(value AS DECIMAL(20,6))', (string) $adapter->castToNumeric('value')); + } + + public function testCreateTableLike(): void + { + $adapter = $this->getMysqlPdoAdapterMock(['query', 'quoteIdentifier']); + $adapter->method('quoteIdentifier')->willReturnCallback(static function ($value) { + return '`' . $value . '`'; + }); + $stmt = $this->createMock(\Zend_Db_Statement_Pdo::class); + $adapter->expects($this->once()) + ->method('query') + ->with('CREATE TABLE `new_table` LIKE `origin_table`') + ->willReturn($stmt); + + $this->assertSame($stmt, $adapter->createTableLike('new_table', 'origin_table')); + } + + public function testCreateTemporaryTableFromSelect(): void + { + $adapter = $this->getMysqlPdoAdapterMock(['query', 'quoteIdentifier']); + $adapter->method('quoteIdentifier')->willReturnCallback(static function ($value) { + return '`' . $value . '`'; + }); + $select = $this->createMock(Select::class); + $select->method('__toString')->willReturn('SELECT 1'); + $select->method('getBind')->willReturn(['foo' => 'bar']); + $stmt = $this->createMock(\Zend_Db_Statement_Pdo::class); + $adapter->expects($this->once()) + ->method('query') + ->with( + 'CREATE TEMPORARY TABLE `tmp` (PRIMARY KEY(id)) ENGINE=`innodb` IGNORE (SELECT 1)', + ['foo' => 'bar'] + ) + ->willReturn($stmt); + + $this->assertSame( + $stmt, + $adapter->createTemporaryTableFromSelect('tmp', ['PRIMARY KEY(id)'], $select) + ); + } } diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/TemporaryTableServiceTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/TemporaryTableServiceTest.php index 15725921c00..daa41e2ecf0 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/TemporaryTableServiceTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/TemporaryTableServiceTest.php @@ -94,33 +94,25 @@ public function testCreateFromSelectWithException() #[DataProvider('createFromSelectDataProvider')] public function testCreateFromSelect($indexes, $expectedSelect) { - $selectString = 'select * from sometable'; $random = 'random_table'; $this->randomMock->expects($this->once()) ->method('getUniqueHash') ->willReturn($random); - $this->adapterMock->expects($this->once()) - ->method('query') - ->with($expectedSelect) - ->willReturnSelf(); - - $this->adapterMock->expects($this->once()) - ->method('query') - ->willReturnSelf(); - $this->adapterMock->expects($this->any()) ->method('quoteIdentifier') ->willReturnArgument(0); - $this->selectMock->expects($this->once()) - ->method('getBind') - ->willReturn(['bind']); - - $this->selectMock->expects($this->any()) - ->method('__toString') - ->willReturn($selectString); + $this->adapterMock->expects($this->once()) + ->method('createTemporaryTableFromSelect') + ->with( + $random, + $this->callback(static function (array $indexStatements) use ($expectedSelect) { + return str_contains($expectedSelect, implode(',', $indexStatements)); + }), + $this->selectMock + ); $this->assertEquals( $random, diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php index c792c83d6b8..1b71c08c19a 100644 --- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php +++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php @@ -736,7 +736,7 @@ protected function _renderOrders() if (!$this->_isOrdersRendered) { foreach ($this->_orders as $field => $direction) { if (isset($this->sqlReservedWords[strtoupper($field)])) { - $field = "`$field`"; + $field = $this->getConnection()->quoteIdentifier($field); } $this->_select->order(new \Zend_Db_Expr($field . ' ' . $direction)); diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php index b7d225f3919..414d3813298 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php @@ -172,6 +172,21 @@ public function testIterations(): void $this->assertCount(3, $result); } + /** + * MAX() over zero rows is SQL NULL. Keep the previous minValue so the next + * WHERE rangeField > ? bind stays an integer (0), not null/''. + */ + public function testEmptyBatchDoesNotBindNullMinValue(): void + { + $filed = $this->correlationName . '.' . $this->rangeField; + $this->connectionMock->method('fetchRow')->willReturn(['max' => null, 'cnt' => 0]); + $this->selectMock->expects($this->exactly(2))->method('where')->with($filed . ' > ?', 0); + + $this->model->current(); + $this->assertFalse($this->model->valid()); + $this->model->next(); + } + /** * Test steps: * 1. $iterator->next(); diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index 17720e6d14e..5942629ae4b 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php @@ -388,7 +388,8 @@ private function validateDbSettings(array $options, DeploymentConfig $deployment $options[ConfigOptionsListConstants::INPUT_KEY_DB_HOST], $options[ConfigOptionsListConstants::INPUT_KEY_DB_USER], $options[ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD], - $driverOptions + $driverOptions, + $options[ConfigOptionsListConstants::INPUT_KEY_DB_ENGINE] ?? null ); } catch (\Exception $exception) { $errors[] = $exception->getMessage(); diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index cc3865d6e50..26e1d3f2160 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -1667,7 +1667,11 @@ private function assertDbAccessible() ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . '/' . ConfigOptionsListConstants::KEY_PASSWORD ), - $driverOptions + $driverOptions, + $this->deploymentConfig->get( + ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . + '/' . ConfigOptionsListConstants::KEY_ENGINE + ) ); $prefix = $this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . diff --git a/setup/src/Magento/Setup/Validator/DbValidator.php b/setup/src/Magento/Setup/Validator/DbValidator.php index 28edec3b2c9..0f6b81c1d7e 100644 --- a/setup/src/Magento/Setup/Validator/DbValidator.php +++ b/setup/src/Magento/Setup/Validator/DbValidator.php @@ -89,6 +89,7 @@ public function checkDatabaseConnection($dbName, $dbHost, $dbUser, $dbPass = '') * @param string $dbUser * @param string $dbPass * @param array $driverOptions + * @param string|null $engine Database engine (mysql, postgresql) * @return bool * @throws \Magento\Setup\Exception */ @@ -97,19 +98,22 @@ public function checkDatabaseConnectionWithDriverOptions( $dbHost, $dbUser, $dbPass = '', - $driverOptions = [] + $driverOptions = [], + $engine = null ) { // establish connection to information_schema view to retrieve information about user and table privileges - $connection = $this->connectionFactory->create( - [ - ConfigOptionsListConstants::KEY_NAME => 'information_schema', - ConfigOptionsListConstants::KEY_HOST => $dbHost, - ConfigOptionsListConstants::KEY_USER => $dbUser, - ConfigOptionsListConstants::KEY_PASSWORD => $dbPass, - ConfigOptionsListConstants::KEY_ACTIVE => true, - ConfigOptionsListConstants::KEY_DRIVER_OPTIONS => $driverOptions, - ] - ); + $connectionConfig = [ + ConfigOptionsListConstants::KEY_NAME => 'information_schema', + ConfigOptionsListConstants::KEY_HOST => $dbHost, + ConfigOptionsListConstants::KEY_USER => $dbUser, + ConfigOptionsListConstants::KEY_PASSWORD => $dbPass, + ConfigOptionsListConstants::KEY_ACTIVE => true, + ConfigOptionsListConstants::KEY_DRIVER_OPTIONS => $driverOptions, + ]; + if ($engine !== null && $engine !== '') { + $connectionConfig[ConfigOptionsListConstants::KEY_ENGINE] = $engine; + } + $connection = $this->connectionFactory->create($connectionConfig); if (!$connection) { throw new \Magento\Setup\Exception('Database connection failure.');