diff --git a/vendor/magento/module-indexer/etc/crontab.xml b/vendor/magento/module-indexer/etc/crontab.xml index 2984f47912f45..a0248a0b3b173 100644 --- a/vendor/magento/module-indexer/etc/crontab.xml +++ b/vendor/magento/module-indexer/etc/crontab.xml @@ -1,8 +1,8 @@ @@ -14,7 +14,7 @@ * * * * * - 0 * * * * + */5 * * * * diff --git a/vendor/magento/module-theme/Model/Indexer/Design/IndexerHandler.php b/vendor/magento/module-theme/Model/Indexer/Design/IndexerHandler.php index 1acc75a6c949c..5a8bf69314e2b 100644 --- a/vendor/magento/module-theme/Model/Indexer/Design/IndexerHandler.php +++ b/vendor/magento/module-theme/Model/Indexer/Design/IndexerHandler.php @@ -1,13 +1,14 @@ flatScopeResolver = $flatScopeResolver; + $this->columnConfig = $dtoFactoriesTable ?: ObjectManager::getInstance()->get(DtoFactoriesTable::class); } /** @@ -67,6 +86,27 @@ public function cleanIndex($dimensions) if ($this->connection->isTableExists($tableName)) { $this->connection->delete($tableName); + // change the charset to utf8mb4 + if ($tableName === self::DESIGN_CONFIG_GRID_FLAT) { + $getTableSchema = $this->connection->showTableStatus($tableName) ?? ''; + if (isset($getTableSchema['Collation']) && + preg_match('/\b('. self::OLDCOLLATION .')\b/', $getTableSchema['Collation'])) { + $charset = $this->columnConfig->getDefaultCharset(); + $collate = $this->columnConfig->getDefaultCollation(); + $columnEncoding = " CHARACTER SET ".$charset." COLLATE ".$collate; + $this->connection->query( + sprintf( + 'ALTER TABLE `%s` MODIFY COLUMN `theme_theme_id` varchar(255) %s %s, + DEFAULT CHARSET=%s, DEFAULT COLLATE=%s', + $tableName, + $columnEncoding, + "COMMENT 'Theme_theme_id'", + $charset, + $collate + ) + ); + } + } } else { $this->indexStructure->create($this->getIndexName(), $this->fields, $dimensions); } diff --git a/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php b/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php index fc84dcafc4688..beeadf1623f77 100644 --- a/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php +++ b/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php @@ -1,7 +1,7 @@ pid = getmypid(); $this->string = $string; $this->dateTime = $dateTime; $this->logger = $logger; $this->selectFactory = $selectFactory; + $this->columnConfig = $dtoFactoriesTable ?: ObjectManager::getInstance()->get(DtoFactoriesTable::class); $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); $this->exceptionMap = [ // SQLSTATE[HY000]: General error: 2006 MySQL server has gone away @@ -1240,7 +1254,11 @@ public function modifyColumn($tableName, $columnName, $definition, $flushData = if (is_array($definition)) { $definition = $this->_getColumnDefinition($definition); } - + // Set default collation to utf8mb4 for MySQL + if (!empty($definition)) { + $type = explode(' ', trim($definition)); + $definition = $this->setDefaultCharsetAndCollation($type[0], $definition, 1); + } $sql = sprintf( 'ALTER TABLE %s MODIFY COLUMN %s %s', $this->quoteIdentifier($tableName), @@ -2404,7 +2422,13 @@ protected function _getColumnsDefinition(Table $table) $columnDefinition ); } - + // Set default collation to utf8mb4 for MySQL + if (count($definition)) { + foreach ($definition as $index => $columnDefinition) { + $type = explode(' ', trim($columnDefinition)); + $definition[$index] = $this->setDefaultCharsetAndCollation($type[1], $columnDefinition, 2); + } + } // PRIMARY KEY if (!empty($primary)) { asort($primary, SORT_NUMERIC); @@ -4249,4 +4273,26 @@ public function __debugInfo() { return []; } + + /*** + * Set default collation & charset (e.g.utf8mb4_general_ci and utf8mb4) for tables + * + * @param string $columnType + * @param string $definition + * @param int $position + * @return string + */ + private function setDefaultCharsetAndCollation($columnType, $definition, $position) : string + { + $pattern = '/\b(' . implode('|', array_map('preg_quote', self::COLUMN_TYPE)) . ')\b/i'; + if (preg_match($pattern, $columnType) === 1) { + $charset = $this->columnConfig->getDefaultCharset(); + $collate = $this->columnConfig->getDefaultCollation(); + $charsets = 'CHARACTER SET ' . $charset. ' COLLATE ' . $collate; + $columnsAttribute = explode(' ', trim($definition)); + array_splice($columnsAttribute, $position, 0, $charsets); + return implode(' ', $columnsAttribute); + } + return $definition; + } } diff --git a/vendor/magento/framework/DB/Ddl/Table.php b/vendor/magento/framework/DB/Ddl/Table.php index 5ba54368c188d..45c9c1c6f0de6 100644 --- a/vendor/magento/framework/DB/Ddl/Table.php +++ b/vendor/magento/framework/DB/Ddl/Table.php @@ -1,11 +1,13 @@ 'INNODB', 'charset' => 'utf8', 'collate' => 'utf8_general_ci']; + /*** + * @var DtoTable|null + */ + private ?DtoTable $DtoTable; + + /*** + * constructor + * + * @param DtoTable|null $DtoTable + */ + public function __construct( + ?DtoTable $DtoTable = null + ) { + $this->DtoTable = $DtoTable ?: ObjectManager::getInstance()->get(DtoTable::class); + } + /** * Set table name * @@ -636,7 +654,7 @@ public function setOption($key, $value) /** * Retrieve table option value by option name * - * Return null if option does not exits + * Return null if option does not exist * * @param string $key * @return null|string @@ -646,6 +664,12 @@ public function getOption($key) if (!isset($this->_options[$key])) { return null; } + if (strtolower($key) == 'charset') { + return $this->DtoTable->getDefaultCharset(); + } + if (strtolower($key) == 'collate') { + return $this->DtoTable->getDefaultCollation(); + } return $this->_options[$key]; } diff --git a/vendor/magento/framework/Mview/View/Changelog.php b/vendor/magento/framework/Mview/View/Changelog.php index c7529a69d2fc6..15f1e78866bff 100644 --- a/vendor/magento/framework/Mview/View/Changelog.php +++ b/vendor/magento/framework/Mview/View/Changelog.php @@ -1,21 +1,23 @@ connection = $resource->getConnection(); $this->resource = $resource; $this->checkConnection(); $this->mviewConfig = $mviewConfig; $this->additionalColumnsProcessorFactory = $additionalColumnsProcessorFactory; + $this->columnConfig = $dtoFactoriesTable ?: ObjectManager::getInstance()->get(DtoFactoriesTable::class); } /** @@ -130,6 +152,21 @@ public function create() } $this->connection->createTable($table); + } else { + // change the charset to utf8mb4 + $getTableSchema = $this->connection->getCreateTable($changelogTableName) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $charset = $this->columnConfig->getDefaultCharset(); + $collate = $this->columnConfig->getDefaultCollation(); + $this->connection->query( + sprintf( + 'ALTER TABLE %s DEFAULT CHARSET=%s, DEFAULT COLLATE=%s', + $changelogTableName, + $charset, + $collate + ) + ); + } } } @@ -191,7 +228,15 @@ public function clear($versionId) throw new ChangelogTableNotExistsException(new Phrase("Table %1 does not exist", [$changelogTableName])); } - $this->connection->delete($changelogTableName, ['version_id < ?' => (int)$versionId]); + $query = sprintf( + 'DELETE FROM `%s` WHERE %s LIMIT %d', + $changelogTableName, + 'version_id < ' . (int) $versionId, + $this->batchSize + ); + do { + $stmt = $this->connection->query($query); + } while ($stmt->rowCount()); return true; } diff --git a/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php b/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php index d8c85ea9e206d..321dbe3ff44fb 100644 --- a/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php +++ b/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php @@ -1,7 +1,7 @@ resourceConnection->getConnection($resource); $dbName = $this->resourceConnection->getSchemaName($resource); + $collationNameColumn = 'charset_applicability.collation_name'; + + /* In case of mariadb>=11.4 check if column FULL_COLLATION_NAME is exist */ + if ($adapter->tableColumnExists( + 'COLLATION_CHARACTER_SET_APPLICABILITY', + 'FULL_COLLATION_NAME', + 'information_schema' + )) { + $collationNameColumn = 'charset_applicability.full_collation_name'; + } + $stmt = $adapter->select() ->from( ['i_tables' => 'information_schema.TABLES'], @@ -65,7 +76,7 @@ public function getTableOptions($tableName, $resource) ) ->joinInner( ['charset_applicability' => 'information_schema.COLLATION_CHARACTER_SET_APPLICABILITY'], - 'i_tables.table_collation = charset_applicability.collation_name', + 'i_tables.table_collation = '.$collationNameColumn, [ 'charset' => 'charset_applicability.CHARACTER_SET_NAME' ] @@ -98,7 +109,9 @@ public function readColumns($tableName, $resource) 'nullable' => new Expression('IF(IS_NULLABLE="YES", true, false)'), 'definition' => 'COLUMN_TYPE', 'extra' => 'EXTRA', - 'comment' => new Expression('IF(COLUMN_COMMENT="", NULL, COLUMN_COMMENT)') + 'comment' => new Expression('IF(COLUMN_COMMENT="", NULL, COLUMN_COMMENT)'), + 'charset' => 'CHARACTER_SET_NAME', + 'collation' => 'COLLATION_NAME' ] ) ->where('TABLE_SCHEMA = ?', $dbName) diff --git a/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaWriter.php b/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaWriter.php index b466eae77d9e2..ead0de2d667f5 100644 --- a/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaWriter.php +++ b/vendor/magento/framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaWriter.php @@ -1,12 +1,17 @@ resourceConnection = $resourceConnection; $this->statementFactory = $statementFactory; $this->dryRunLogger = $dryRunLogger; + $this->columnConfig = $dtoFactoriesTable ?: ObjectManager::getInstance()->get(DtoFactoriesTable::class); $this->tableOptions = array_replace($this->tableOptions, $tableOptions); + $this->sqlVersionProvider = $sqlVersionProvider; } /** @@ -82,6 +108,15 @@ public function __construct( */ public function createTable($tableName, $resource, array $definition, array $options) { + if (count($definition)) { + foreach ($definition as $index => $value) { + if ($this->isColumnExists($value, self::COLUMN_TYPE)) { + if (str_contains($index, 'column')) { + $definition[$index] = $this->setDefaultCharsetAndCollation($value); + } + } + } + } $sql = sprintf( "(\n%s\n) ENGINE=%s DEFAULT CHARSET=%s DEFAULT COLLATE=%s %s", implode(", \n", $definition), @@ -202,6 +237,10 @@ public function modifyTableOption($tableName, $resource, $optionName, $optionVal */ public function modifyColumn($columnName, $resource, $tableName, $columnDefinition) { + if ($this->isColumnExists($columnDefinition, self::COLUMN_TYPE)) { + $columnDefinition = $this->setDefaultCharsetAndCollation($columnDefinition); + } + $sql = sprintf( 'MODIFY COLUMN %s', $columnDefinition @@ -271,15 +310,14 @@ public function compile(StatementAggregator $statementAggregator, $dryRun) $statementsSql = []; $statement = null; - /** - * @var Statement $statement - */ - foreach ($statementBank as $statement) { - $statementsSql[] = $statement->getStatement(); - } - $adapter = $this->resourceConnection->getConnection($statement->getResource()); - if ($dryRun) { + /** + * @var Statement $statement + */ + foreach ($statementBank as $statement) { + $statementsSql[] = $statement->getStatement(); + } + $adapter = $this->resourceConnection->getConnection($statement->getResource()); $this->dryRunLogger->log( sprintf( $this->statementDirectives[$statement->getType()], @@ -288,18 +326,82 @@ public function compile(StatementAggregator $statementAggregator, $dryRun) ) ); } else { + $this->doQuery($statementBank); + $statement = end($statementBank); + //Do post update, like SQL DML operations or etc... + foreach ($statement->getTriggers() as $trigger) { + call_user_func($trigger); + } + } + } + } + + /** + * Check if we can concatenate sql into one statement + * + * Due to issues with some versions of MariaBD such statements + * may produce errors, e.g. with foreign key definition with column modification + * + * @return bool + * @throws ConnectionException + */ + private function isNeedToSplitSql() : bool + { + return str_contains($this->sqlVersionProvider->getSqlVersion(), SqlVersionProvider::MARIA_DB_10_4_VERSION) || + str_contains($this->sqlVersionProvider->getSqlVersion(), SqlVersionProvider::MARIA_DB_10_6_VERSION) || + str_contains($this->sqlVersionProvider->getSqlVersion(), SqlVersionProvider::MARIA_DB_11_4_VERSION); + } + + /** + * Perform queries based on statements + * + * @param Statement[] $statementBank + * @return void + * @throws ConnectionException + */ + private function doQuery( + array $statementBank + ) : void { + if (empty($statementBank)) { + return; + } + + $statement = null; + $statementsSql = []; + foreach ($statementBank as $statement) { + $statementsSql[] = $statement->getStatement(); + } + $adapter = $this->resourceConnection->getConnection($statement->getResource()); + + if ($this->isNeedToSplitSql()) { + $preparedStatements = $this->getPreparedStatements($statementBank); + + if (!empty($preparedStatements['canBeCombinedStatements'])) { $adapter->query( sprintf( $this->statementDirectives[$statement->getType()], $adapter->quoteIdentifier($statement->getTableName()), - implode(", ", $statementsSql) + implode(", ", $preparedStatements['canBeCombinedStatements']) ) ); - //Do post update, like SQL DML operations or etc... - foreach ($statement->getTriggers() as $trigger) { - call_user_func($trigger); - } } + foreach ($preparedStatements['separatedStatements'] as $separatedStatement) { + $adapter->query( + sprintf( + $this->statementDirectives[$statement->getType()], + $adapter->quoteIdentifier($statement->getTableName()), + $separatedStatement + ) + ); + } + } else { + $adapter->query( + sprintf( + $this->statementDirectives[$statement->getType()], + $adapter->quoteIdentifier($statement->getTableName()), + implode(", ", $statementsSql) + ) + ); } } @@ -309,6 +411,7 @@ public function compile(StatementAggregator $statementAggregator, $dryRun) * @param string $tableName * @param string $resource * @return int + * @throws \Zend_Db_Statement_Exception */ private function getNextAutoIncrementValue(string $tableName, string $resource): int { @@ -323,6 +426,89 @@ private function getNextAutoIncrementValue(string $tableName, string $resource): } else { return 1; } + } + + /** + * Prepare list of modified columns from statement + * + * @param array $statementBank + * @return array + */ + private function getModifiedColumns(array $statementBank) : array + { + $columns = []; + foreach ($statementBank as $statement) { + if ($statement->getType() === 'alter' + && str_contains($statement->getStatement(), 'MODIFY COLUMN')) { + $columns[] = $statement->getName(); + } + } + return $columns; + } + + /** + * Separate statements that can't be executed as one statement + * + * @param array $statementBank + * @return array + */ + private function getPreparedStatements(array $statementBank) : array + { + $statementsSql = []; + foreach ($statementBank as $statement) { + $statementsSql[] = $statement->getStatement(); + } + $result = ['separatedStatements' => [], 'canBeCombinedStatements' => []]; + $modifiedColumns = $this->getModifiedColumns($statementBank); + foreach ($statementsSql as $statementSql) { + if (str_contains($statementSql, 'FOREIGN KEY')) { + $isThisColumnModified = false; + foreach ($modifiedColumns as $modifiedColumn) { + if (str_contains($statementSql, '`' . $modifiedColumn . '`')) { + $isThisColumnModified = true; + break; + } + } + if ($isThisColumnModified) { + $result['separatedStatements'][] = $statementSql; + } else { + $result['canBeCombinedStatements'][] = $statementSql; + } + } else { + $result['canBeCombinedStatements'][] = $statementSql; + } + } + return $result; + } + + /** + * Set default collation & charset (e.g. utf8mb4_general_ci and utf8mb4) for tables + * + * @param string $columnDefinition + * @return string + */ + private function setDefaultCharsetAndCollation(string $columnDefinition): string + { + $charset = $this->columnConfig->getDefaultCharset(); + $collate = $this->columnConfig->getDefaultCollation(); + $columnLevelConfig = 'CHARACTER SET ' . $charset . ' COLLATE ' . $collate; + $columnsAttribute = explode(' ', $columnDefinition); + array_splice($columnsAttribute, 2, 0, $columnLevelConfig); + return implode(" ", $columnsAttribute); + } + + /** + * Checks if any column of type varchar,char or text (mediumtext/longtext) + * + * @param string $definition + * @param array $columntypes + * @return bool + */ + private function isColumnExists(string $definition, array $columntypes): bool + { + $type = explode(' ', $definition); + $pattern = '/\b(' . implode('|', array_map('preg_quote', $columntypes)) . ')\b/i'; + return preg_match($pattern, $type[1]) === 1; } } diff --git a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php index 4de198ae631f4..e8c2fbe0f6eab 100644 --- a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php +++ b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/StringBinary.php @@ -1,7 +1,7 @@ nullable = $nullable; $this->default = $default; $this->length = $length; + $this->charset = $charset; + $this->collation = $collation; } /** @@ -92,17 +110,42 @@ public function getLength() return $this->length; } + /** + * Get collation + * + * @return string|null + */ + public function getCollation(): ?string + { + return $this->collation; + } + + /** + * Get charset + * + * @return string|null + */ + public function getCharset(): ?string + { + return $this->charset; + } + /** * @inheritdoc */ public function getDiffSensitiveParams() { - return [ + $param = [ 'type' => $this->getType(), 'nullable' => $this->isNullable(), 'default' => $this->getDefault(), 'length' => $this->getLength(), 'comment' => $this->getComment() ]; + if ($this->getType() === 'varchar') { + $param['collation'] = $this->getCollation(); + $param['charset'] = $this->getCharset(); + } + return $param; } } diff --git a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/Text.php b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/Text.php index b127d5d50e08c..750027490c470 100644 --- a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/Text.php +++ b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Columns/Text.php @@ -1,7 +1,7 @@ nullable = $nullable; + $this->charset = $charset; + $this->collation = $collation; } /** @@ -54,6 +70,26 @@ public function isNullable() return $this->nullable; } + /** + * Get collation + * + * @return string|null + */ + public function getCollation(): ?string + { + return $this->collation; + } + + /** + * Get charset + * + * @return string|null + */ + public function getCharset(): ?string + { + return $this->charset; + } + /** * @inheritdoc */ @@ -62,7 +98,9 @@ public function getDiffSensitiveParams() return [ 'type' => $this->getType(), 'nullable' => $this->isNullable(), - 'comment' => $this->getComment() + 'comment' => $this->getComment(), + 'collation' => $this->getCollation(), + 'charset' => $this->getCharset() ]; } } diff --git a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Factories/Table.php b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Factories/Table.php index 3fde8398fccf8..86e980b52ced9 100644 --- a/vendor/magento/framework/Setup/Declaration/Schema/Dto/Factories/Table.php +++ b/vendor/magento/framework/Setup/Declaration/Schema/Dto/Factories/Table.php @@ -1,7 +1,7 @@ 'utf8', - '10.6.' => 'utf8mb3', - '10.11.' => 'utf8mb3', - 'mysql_8_29' => 'utf8mb3', + '10.4.' => 'utf8mb4', + '10.6.' => 'utf8mb4', + '11.4.' => 'utf8mb4', + 'mysql_8_29' => 'utf8mb4', 'default' => 'utf8' ]; @@ -59,10 +59,10 @@ class Table implements FactoryInterface * @var array|string[] */ private static array $defaultCollation = [ - '10.4.' => 'utf8_general_ci', - '10.6.' => 'utf8mb3_general_ci', - '10.11.' => 'utf8mb3_general_ci', - 'mysql_8_29' => 'utf8mb3_general_ci', + '10.4.' => 'utf8mb4_general_ci', + '10.6.' => 'utf8mb4_general_ci', + '11.4.' => 'utf8mb4_general_ci', + 'mysql_8_29' => 'utf8mb4_general_ci', 'default' => 'utf8_general_ci' ]; @@ -124,7 +124,7 @@ public function create(array $data) * * @return string */ - private function getDefaultCharset(): string + public function getDefaultCharset(): string { if ($this->sqlVersionProvider->isMysqlGte8029()) { return self::$defaultCharset['mysql_8_29']; @@ -139,7 +139,7 @@ private function getDefaultCharset(): string * * @return string */ - private function getDefaultCollation(): string + public function getDefaultCollation(): string { if ($this->sqlVersionProvider->isMysqlGte8029()) { return self::$defaultCollation['mysql_8_29']; diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index 67bb890cce77e..8ca000ff61da0 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -1,7 +1,7 @@ filePermissions = $filePermissions; $this->deploymentConfigWriter = $deploymentConfigWriter; @@ -338,6 +351,8 @@ public function __construct( * from that ObjectManager gets reset as different steps in the installer will write to the deployment config. */ $this->firstDeploymentConfig = ObjectManager::getInstance()->get(DeploymentConfig::class); + $this->columnConfig = $dtoFactoriesTable ?: ObjectManager::getInstance()->get(DtoFactoriesTable::class); + ; } /** @@ -644,6 +659,16 @@ private function setupModuleRegistry(SchemaSetupInterface $setup) 'Data Version' )->setComment('Module versions registry'); $connection->createTable($table); + } else { + // Set default collation to utf8mb4 for MySQL + $getTableSchema = $connection->getCreateTable($setup->getTable('setup_module')) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $tableName = $setup->getTable('setup_module'); + $columns = ['module' => ['varchar(50)',''], + 'schema_version' => ['varchar(50)',''], + 'data_version' => ['varchar(50)','']]; + $this->setDefaultCharsetAndCollation($tableName, $columns, $connection); + } } } @@ -703,6 +728,14 @@ private function setupSessionTable( 'Database Sessions Storage' ); $connection->createTable($table); + } else { + // Set default collation to utf8mb4 for MySQL + $getTableSchema = $connection->getCreateTable($setup->getTable('session')) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $tableName = $setup->getTable('session'); + $columns = ['session_id' => ['varchar(255)','']]; + $this->setDefaultCharsetAndCollation($tableName, $columns, $connection); + } } } @@ -758,6 +791,14 @@ private function setupCacheTable( 'Caches' ); $connection->createTable($table); + } else { + // change the charset to utf8mb4 + $getTableSchema = $connection->getCreateTable($setup->getTable('cache')) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $tableName = $setup->getTable('cache'); + $columns = ['id' => ['varchar(200)','']]; + $this->setDefaultCharsetAndCollation($tableName, $columns, $connection); + } } } @@ -795,6 +836,14 @@ private function setupCacheTagTable( 'Tag Caches' ); $connection->createTable($table); + } else { + // Set default collation to utf8mb4 for MySQL + $getTableSchema = $connection->getCreateTable($setup->getTable('cache_tag')) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $tableName = $setup->getTable('cache_tag'); + $columns = ['tag' => ['varchar(100)',''],'cache_id' => ['varchar(200)','']]; + $this->setDefaultCharsetAndCollation($tableName, $columns, $connection); + } } } @@ -853,6 +902,12 @@ private function setupFlagTable( $connection->createTable($table); } else { $this->updateColumnType($connection, $tableName, 'flag_data', 'mediumtext'); + // change the charset to utf8mb4 + $getTableSchema = $connection->getCreateTable($tableName) ?? ''; + if (preg_match('/\b('. self::OLDCHARSET .')\b/', $getTableSchema)) { + $columns = ['flag_code' => ['varchar(255)','NOT NULL'],'flag_data' => ['mediumtext','']]; + $this->setDefaultCharsetAndCollation($tableName, $columns, $connection); + } } } @@ -1827,4 +1882,25 @@ private function setIndexerModeSchedule(): void $this->log->log(__("We couldn't change indexer(s)' mode because of an error: ".$e->getMessage())); } } + + /** + * Set default collation & charset (e.g. utf8mb4_general_ci and utf8mb4) for core setup tables + * + * @param string $tableName + * @param array $columns + * @param AdapterInterface $connection + * @return void + */ + private function setDefaultCharsetAndCollation(string $tableName, array $columns, $connection) : void + { + $charset = $this->columnConfig->getDefaultCharset(); + $collate = $this->columnConfig->getDefaultCollation(); + $encoding = " CHARACTER SET ".$charset." COLLATE ".$collate; + $qry = sprintf('ALTER TABLE %s ', $tableName); + foreach ($columns as $key => $prop) { + $qry .= "MODIFY COLUMN `$key` $prop[0] $encoding $prop[1], "; + } + $qry .= sprintf('DEFAULT CHARSET=%s, DEFAULT COLLATE=%s', $charset, $collate); + $connection->query($qry); + } }