diff --git a/app/src/Lib/Util/CmgMySQLSchemaManager.php b/app/src/Lib/Util/CmgMySQLSchemaManager.php new file mode 100644 index 000000000..384a31a48 --- /dev/null +++ b/app/src/Lib/Util/CmgMySQLSchemaManager.php @@ -0,0 +1,76 @@ + $v) { + $v = array_change_key_case($v, CASE_LOWER); + if ($v['key_name'] === 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = ['FULLTEXT']; + } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + $v['flags'] = ['SPATIAL']; + } + + // Ignore prohibited prefix `length` for spatial index + if (strpos($v['index_type'], 'SPATIAL') === false) { + $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; + } + + $tableIndexes[$k] = $v; + } + + return AbstractSchemaManager::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result + { + $sql = 'SELECT'; + + if ($tableName === null) { + $sql .= ' TABLE_NAME,'; + } + + $sql .= <<<'SQL' + NON_UNIQUE AS Non_Unique, + INDEX_NAME AS Key_name, + CONCAT(COALESCE(COLUMN_NAME, ''), COALESCE(EXPRESSION, '')) AS Column_Name, + SUB_PART AS Sub_Part, + INDEX_TYPE AS Index_Type, + EXPRESSION AS Expression +FROM information_schema.STATISTICS +SQL; + + $conditions = ['TABLE_SCHEMA = ?']; + $params = [$databaseName]; + + if ($tableName !== null) { + $conditions[] = 'TABLE_NAME = ?'; + $params[] = $tableName; + } + + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; + + return $this->_conn->executeQuery($sql, $params); + } + +} \ No newline at end of file diff --git a/app/src/Lib/Util/CmgPostgreSQLSchemaManager.php b/app/src/Lib/Util/CmgPostgreSQLSchemaManager.php index d557def5a..ec3e1c20e 100644 --- a/app/src/Lib/Util/CmgPostgreSQLSchemaManager.php +++ b/app/src/Lib/Util/CmgPostgreSQLSchemaManager.php @@ -20,7 +20,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null foreach ($tableIndexes as $row) { $colNumbers = array_map('intval', explode(' ', $row['indkey'])); $colNames = explode("," , trim($row["indkey_names"], "{,}")); - $colNumName = array_combine($colNumbers, $colNames); + $colNumName = array_combine($colNumbers, $colNames); // required for getting the order of the columns right. foreach ($colNumName as $colNum => $colName) { diff --git a/app/src/Lib/Util/CmgSchemaManagerFactory.php b/app/src/Lib/Util/CmgSchemaManagerFactory.php index 4d54e7fa3..e373951e6 100644 --- a/app/src/Lib/Util/CmgSchemaManagerFactory.php +++ b/app/src/Lib/Util/CmgSchemaManagerFactory.php @@ -3,8 +3,11 @@ namespace App\Lib\Util; use App\Lib\Util\CmgPostgreSQLSchemaManager; +use App\Lib\Util\CmgMySQLSchemaManager; use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MySQL80Platform; +use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\SchemaManagerFactory; @@ -29,6 +32,10 @@ public function createSchemaManager(Connection $connection): AbstractSchemaManag return new CmgPostgreSQLSchemaManager($connection, $platform); } + if ($platform instanceof MySQLPlatform) { + return new CmgMySQLSchemaManager($connection, $platform); + } + return $this->defaultFactory->createSchemaManager($connection); } } diff --git a/app/src/Lib/Util/SchemaManager.php b/app/src/Lib/Util/SchemaManager.php index ee53ab284..888b560f1 100644 --- a/app/src/Lib/Util/SchemaManager.php +++ b/app/src/Lib/Util/SchemaManager.php @@ -271,9 +271,23 @@ protected function processSchema( // will be executed later. What we actually do is treat the expression // as a temporary virtual column // https://www.postgresql.org/docs/14/indexes-expressional.html + // https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-functional-key-parts + // https://www.sqlite.org/expridx.html + // XXX MariadBD is not supported $tempColumns = []; - foreach ($iCfg->columns as $clm_name) { + foreach ($iCfg->columns as $idx => $clm_name) { if (! $table->hasColumn($clm_name)) { + if(in_array($this->driver, array( + "Cake\Database\Driver\Mysql", + "Cake\Database\Driver\Sqlite", + ))) { + // MySQL requires the column name to be wrapped with parenthesis + // Instead of indexing a simple column, you can create the index on the result + // of any function applied to a column or multiple columns. + // Be aware of the double parentheses. The syntax is correct since the expression + // must be enclosed within parentheses to distinguish it from columns or column prefixes. + $iCfg->columns[$idx] = $clm_name = "({$clm_name})"; + } $table->addColumn($clm_name, "string"); $tempColumns[] = $clm_name; } @@ -294,7 +308,7 @@ protected function processSchema( // (For Registry) If an attribute is "sourced" it is a CO Person attribute // that is copied via a Pipeline from an External Identity that was created from - // an External Identity Source, so we need a foreign key into ourself. + // an External Identity Source, so we need a foreign key into ourselves. if(isset($tCfg->sourced) && $tCfg->sourced) { $sColumn = "source_" . $tablePrefix.\Cake\Utility\Inflector::singularize($tName) . "_id"; @@ -332,7 +346,7 @@ protected function processSchema( // SchemaManager provides info about the database $sm = $this->conn->createSchemaManager(); - // The is the current database representation + // This is the current database representation $curSchema = $sm->introspectSchema(); $fromSql = $curSchema->toSql($this->conn->getDatabasePlatform());