From da9a6ba2a1ca6d5ec7ccd8bc8e2e3bbb032efaea Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 16 Sep 2022 16:01:05 -0700 Subject: [PATCH 01/20] Add ability to set key names for primary, key, and unique key Mysql and Sqlite Forgot to commit this in smaller steps.. --- system/Database/Forge.php | 124 +++++++++++++++-------- system/Database/MySQLi/Forge.php | 30 +++--- system/Database/SQLite3/Forge.php | 39 +------ tests/system/Database/Live/ForgeTest.php | 2 + 4 files changed, 100 insertions(+), 95 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index eabed06ed6ec..282f01f26ec2 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -39,7 +39,7 @@ class Forge /** * List of keys. * - * @var array + * @phpstan-var array{}|list */ protected $keys = []; @@ -51,9 +51,9 @@ class Forge protected $uniqueKeys = []; /** - * List of primary keys. + * Primary keys. * - * @var array + * @phpstan-var array{}|array{fields: string[], keyName: string} */ protected $primaryKeys = []; @@ -309,14 +309,12 @@ public function dropDatabase(string $dbName): bool * * @return Forge */ - public function addKey($key, bool $primary = false, bool $unique = false) + public function addKey($key, bool $primary = false, bool $unique = false, string $keyName = '') { if ($primary) { - foreach ((array) $key as $one) { - $this->primaryKeys[] = $one; - } + $this->primaryKeys = ['fields' => (array) $key, 'keyName' => $keyName]; } else { - $this->keys[] = $key; + $this->keys[] = ['fields' => (array) $key, 'keyName' => $keyName]; if ($unique) { $this->uniqueKeys[] = count($this->keys) - 1; @@ -333,9 +331,9 @@ public function addKey($key, bool $primary = false, bool $unique = false) * * @return Forge */ - public function addPrimaryKey($key) + public function addPrimaryKey($key, string $keyName = '') { - return $this->addKey($key, true); + return $this->addKey($key, true, false, $keyName); } /** @@ -345,9 +343,9 @@ public function addPrimaryKey($key) * * @return Forge */ - public function addUniqueKey($key) + public function addUniqueKey($key, string $keyName = '') { - return $this->addKey($key, false, true); + return $this->addKey($key, false, true, $keyName); } /** @@ -404,11 +402,9 @@ public function addField($field) * @param string|string[] $fieldName * @param string|string[] $tableField * - * @return Forge - * * @throws DatabaseException */ - public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = '') + public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = ''): Forge { $fieldName = (array) $fieldName; $tableField = (array) $tableField; @@ -441,17 +437,27 @@ public function addForeignKey($fieldName = '', string $tableName = '', $tableFie /** * Drop Key * - * @return bool - * * @throws DatabaseException */ - public function dropKey(string $table, string $keyName) + public function dropKey(string $table, string $keyName, bool $prefixKeyName = true): bool { - $sql = sprintf( - $this->dropIndexStr, - $this->db->escapeIdentifiers($this->db->DBPrefix . $keyName), - $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - ); + $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); + $table = $this->db->escapeIdentifiers($this->db->DBPrefix . $table); + $dropKeyAsConstraint = $this->dropKeyAsConstraint($table, $keyName); + + if ($dropKeyAsConstraint === true) { + $sql = sprintf( + $this->dropConstraintStr, + $table, + $keyName, + ); + } else { + $sql = sprintf( + $this->dropIndexStr, + $keyName, + $table, + ); + } if ($sql === '') { if ($this->db->DBDebug) { @@ -464,15 +470,37 @@ public function dropKey(string $table, string $keyName) return $this->db->query($sql); } + /** + * Checks if if key needs to be dropped as a constraint. + */ + protected function dropKeyAsConstraint(string $table, string $constraintName): bool + { + $sql = $this->_dropKeyAsConstraint($table, $constraintName); + + if ($sql === '') { + return false; + } + + return $this->db->query($sql)->getResultArray() !== []; + } + + /** + * Constructs sql to check if key is a constraint. + */ + protected function _dropKeyAsConstraint(string $table, string $constraintName): string + { + return ''; + } + /** * Drop Primary Key */ - public function dropPrimaryKey(string $table): bool + public function dropPrimaryKey(string $table, string $keyName = ''): bool { $sql = sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - $this->db->escapeIdentifiers('pk_' . $this->db->DBPrefix . $table), + ($keyName === '') ? $this->db->escapeIdentifiers('pk_' . $this->db->DBPrefix . $table) : $this->db->escapeIdentifiers($keyName), ); return $this->db->query($sql); @@ -1016,15 +1044,19 @@ protected function _processPrimaryKeys(string $table): string { $sql = ''; - for ($i = 0, $c = count($this->primaryKeys); $i < $c; $i++) { - if (! isset($this->fields[$this->primaryKeys[$i]])) { - unset($this->primaryKeys[$i]); + if (isset($this->primaryKeys['fields'])) { + for ($i = 0, $c = count($this->primaryKeys['fields']); $i < $c; $i++) { + if (! isset($this->fields[$this->primaryKeys['fields'][$i]])) { + unset($this->primaryKeys['fields'][$i]); + } } } - if ($this->primaryKeys !== []) { - $sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers('pk_' . $table) - . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys)) . ')'; + if (isset($this->primaryKeys['fields']) && $this->primaryKeys['fields'] !== []) { + $sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers(($this->primaryKeys['keyName'] === '' ? + 'pk_' . $table : + $this->primaryKeys['keyName'])) + . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys['fields'])) . ')'; } return $sql; @@ -1035,29 +1067,37 @@ protected function _processIndexes(string $table) $sqls = []; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - $this->keys[$i] = (array) $this->keys[$i]; - - for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) { - if (! isset($this->fields[$this->keys[$i][$i2]])) { - unset($this->keys[$i][$i2]); + for ($i2 = 0, $c2 = count($this->keys[$i]['fields']); $i2 < $c2; $i2++) { + if (! isset($this->fields[$this->keys[$i]['fields'][$i2]])) { + unset($this->keys[$i]['fields'][$i2]); } } - if (count($this->keys[$i]) <= 0) { + if (count($this->keys[$i]['fields']) <= 0) { continue; } + $keyName = $this->db->escapeIdentifiers(($this->keys[$i]['keyName'] === '') ? + $table . '_' . implode('_', $this->keys[$i]['fields']) : + $this->keys[$i]['keyName']); + if (in_array($i, $this->uniqueKeys, true)) { - $sqls[] = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) - . ' ADD CONSTRAINT ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) - . ' UNIQUE (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; + if ($this->db->DBDriver === 'SQLite3') { + $sqls[] = 'CREATE UNIQUE INDEX ' . $keyName + . ' ON ' . $this->db->escapeIdentifiers($table) + . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ')'; + } else { + $sqls[] = 'ALTER TABLE ' . $this->db->escapeIdentifiers($table) + . ' ADD CONSTRAINT ' . $keyName + . ' UNIQUE (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ')'; + } continue; } - $sqls[] = 'CREATE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) + $sqls[] = 'CREATE INDEX ' . $keyName . ' ON ' . $this->db->escapeIdentifiers($table) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; + . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ')'; } return $sqls; diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 12aab402d647..6adcbeb9ce64 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -192,28 +192,28 @@ protected function _processIndexes(string $table): string $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if (is_array($this->keys[$i])) { - for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) { - if (! isset($this->fields[$this->keys[$i][$i2]])) { - unset($this->keys[$i][$i2]); + if (isset($this->keys[$i]['fields'])) { + for ($i2 = 0, $c2 = count($this->keys[$i]['fields']); $i2 < $c2; $i2++) { + if (! isset($this->fields[$this->keys[$i]['fields'][$i2]])) { + unset($this->keys[$i]['fields'][$i2]); continue; } } - } elseif (! isset($this->fields[$this->keys[$i]])) { - unset($this->keys[$i]); - - continue; } - if (! is_array($this->keys[$i])) { - $this->keys[$i] = [$this->keys[$i]]; + if (! is_array($this->keys[$i]['fields'])) { + $this->keys[$i]['fields'] = [$this->keys[$i]['fields']]; } $unique = in_array($i, $this->uniqueKeys, true) ? 'UNIQUE ' : ''; - $sql .= ",\n\t{$unique}KEY " . $this->db->escapeIdentifiers(implode('_', $this->keys[$i])) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ')'; + $keyName = $this->db->escapeIdentifiers(($this->keys[$i]['keyName'] === '') ? + implode('_', $this->keys[$i]['fields']) : + $this->keys[$i]['keyName']); + + $sql .= ",\n\t{$unique}KEY " . $keyName + . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ')'; } $this->keys = []; @@ -223,10 +223,8 @@ protected function _processIndexes(string $table): string /** * Drop Key - * - * @return bool */ - public function dropKey(string $table, string $keyName) + public function dropKey(string $table, string $keyName, bool $prefixKeyName = true): bool { $sql = sprintf( $this->dropIndexStr, @@ -240,7 +238,7 @@ public function dropKey(string $table, string $keyName) /** * Drop Primary Key */ - public function dropPrimaryKey(string $table): bool + public function dropPrimaryKey(string $table, string $keyName = ''): bool { $sql = sprintf( 'ALTER TABLE %s DROP PRIMARY KEY', diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 5538777f610b..0de70960fc0a 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -156,41 +156,6 @@ protected function _processColumn(array $field): string . $field['default']; } - /** - * Process indexes - */ - protected function _processIndexes(string $table): array - { - $sqls = []; - - for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - $this->keys[$i] = (array) $this->keys[$i]; - - for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) { - if (! isset($this->fields[$this->keys[$i][$i2]])) { - unset($this->keys[$i][$i2]); - } - } - if (count($this->keys[$i]) <= 0) { - continue; - } - - if (in_array($i, $this->uniqueKeys, true)) { - $sqls[] = 'CREATE UNIQUE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) - . ' ON ' . $this->db->escapeIdentifiers($table) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');'; - - continue; - } - - $sqls[] = 'CREATE INDEX ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) - . ' ON ' . $this->db->escapeIdentifiers($table) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');'; - } - - return $sqls; - } - /** * Field attribute TYPE * @@ -254,7 +219,7 @@ public function dropForeignKey(string $table, string $foreignName): bool /** * Drop Primary Key */ - public function dropPrimaryKey(string $table): bool + public function dropPrimaryKey(string $table, string $keyName = ''): bool { $sqlTable = new Table($this->db, $this); @@ -263,7 +228,7 @@ public function dropPrimaryKey(string $table): bool ->run(); } - public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = '') + public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = ''): BaseForge { if ($fkName === '') { return parent::addForeignKey($fieldName, $tableName, $tableField, $onUpdate, $onDelete, $fkName); diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 978550939b09..cc709c0fe5df 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -1047,6 +1047,8 @@ public function testAddFields() public function testCompositeKey() { + $this->forge->dropTable('forge_test_1', true); + $this->forge->addField([ 'id' => [ 'type' => 'INTEGER', From e53c0eb92e770846b37d5e0180a86b65acb72336 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 11:44:33 -0700 Subject: [PATCH 02/20] Add index name to sqlsrv --- system/Database/MySQLi/Forge.php | 2 ++ system/Database/SQLSRV/Forge.php | 38 ++++++++++++++++++------------- system/Database/SQLite3/Forge.php | 2 ++ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index 6adcbeb9ce64..b3069f2b4cb7 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -237,6 +237,8 @@ public function dropKey(string $table, string $keyName, bool $prefixKeyName = tr /** * Drop Primary Key + * + * @param mixed $keyName */ public function dropPrimaryKey(string $table, string $keyName = ''): bool { diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index e9625018cb00..b1b54e02c207 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -250,31 +250,35 @@ protected function _processIndexes(string $table) $sqls = []; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - $this->keys[$i] = (array) $this->keys[$i]; + $this->keys[$i]['fields'] = (array) $this->keys[$i]['fields']; - for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) { - if (! isset($this->fields[$this->keys[$i][$i2]])) { - unset($this->keys[$i][$i2]); + for ($i2 = 0, $c2 = count($this->keys[$i]['fields']); $i2 < $c2; $i2++) { + if (! isset($this->fields[$this->keys[$i]['fields'][$i2]])) { + unset($this->keys[$i]['fields'][$i2]); } } - if (count($this->keys[$i]) <= 0) { + if (count($this->keys[$i]['fields']) <= 0) { continue; } + $keyName = ($this->keys[$i]['keyName'] === '') ? + $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i]['fields'])) : + $this->db->escapeIdentifiers($this->keys[$i]['keyName']); + if (in_array($i, $this->uniqueKeys, true)) { $sqls[] = 'ALTER TABLE ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table) - . ' ADD CONSTRAINT ' . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) - . ' UNIQUE (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');'; + . ' ADD CONSTRAINT ' . $keyName + . ' UNIQUE (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ');'; continue; } $sqls[] = 'CREATE INDEX ' - . $this->db->escapeIdentifiers($table . '_' . implode('_', $this->keys[$i])) + . $keyName . ' ON ' . $this->db->escapeIdentifiers($this->db->schema) . '.' . $this->db->escapeIdentifiers($table) - . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i])) . ');'; + . ' (' . implode(', ', $this->db->escapeIdentifiers($this->keys[$i]['fields'])) . ');'; } return $sqls; @@ -300,15 +304,17 @@ protected function _processColumn(array $field): string */ protected function _processPrimaryKeys(string $table): string { - for ($i = 0, $c = count($this->primaryKeys); $i < $c; $i++) { - if (! isset($this->fields[$this->primaryKeys[$i]])) { - unset($this->primaryKeys[$i]); + if (isset($this->primaryKeys['fields'])) { + for ($i = 0, $c = count($this->primaryKeys['fields']); $i < $c; $i++) { + if (! isset($this->fields[$this->primaryKeys['fields'][$i]])) { + unset($this->primaryKeys['fields'][$i]); + } } - } - if ($this->primaryKeys !== []) { - $sql = ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers('pk_' . $table) - . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys)) . ')'; + if ($this->primaryKeys['fields'] !== []) { + $sql = ",\n\tCONSTRAINT " . ($this->primaryKeys['keyName'] === '' ? $this->db->escapeIdentifiers('pk_' . $table) : $this->primaryKeys['keyName']) + . ' PRIMARY KEY(' . implode(', ', $this->db->escapeIdentifiers($this->primaryKeys['fields'])) . ')'; + } } return $sql ?? ''; diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index 0de70960fc0a..0098bc250cf7 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -218,6 +218,8 @@ public function dropForeignKey(string $table, string $foreignName): bool /** * Drop Primary Key + * + * @param mixed $keyName */ public function dropPrimaryKey(string $table, string $keyName = ''): bool { From 63a969ef137615ae195c4879588b12af434bcefe Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 12:27:09 -0700 Subject: [PATCH 03/20] Add index name to OCI8 --- system/Database/OCI8/Forge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 8acbd246c063..5c9b549fe2bc 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -186,7 +186,7 @@ protected function _processColumn(array $field): string . ' IN ' . $field['length'] . ')'; $field['length'] = '(' . max(array_map('mb_strlen', explode("','", mb_substr($field['length'], 2, -2)))) . ')' . $constraint; - } elseif (count($this->primaryKeys) === 1 && $field['name'] === $this->primaryKeys[0]) { + } elseif (isset($this->primaryKeys['fields']) && count($this->primaryKeys['fields']) === 1 && $field['name'] === $this->primaryKeys['fields'][0]) { $field['unique'] = ''; } From 3ad29908b91e3be9c88cdeac6a5c933502e7f862 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 14:19:57 -0700 Subject: [PATCH 04/20] Created dropKey() methohd for OCI8 Some keys such as unique indexes are created as constraints. This looks to see if there is a constraint by the index name. If there is then we will drop constraint else we drop index. --- system/Database/OCI8/Forge.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 5c9b549fe2bc..15cb77f6b54a 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -279,4 +279,14 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) return $sql; } + + /** + * Constructs sql to check if key is a constraint. + */ + protected function _dropKeyAsConstraint(string $table, string $constraintName): string + { + return "SELECT constraint_name FROM all_constraints WHERE table_name = '" + . trim($table, '"') . "' AND index_name = '" + . trim($constraintName, '"') . "'"; + } } From 6d8648f6231645b66027c72950bd55b2da2bdec0 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 14:43:57 -0700 Subject: [PATCH 05/20] Add prefixKeyName to mysql dropKey() --- system/Database/MySQLi/Forge.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/Database/MySQLi/Forge.php b/system/Database/MySQLi/Forge.php index b3069f2b4cb7..6adcbeb9ce64 100644 --- a/system/Database/MySQLi/Forge.php +++ b/system/Database/MySQLi/Forge.php @@ -237,8 +237,6 @@ public function dropKey(string $table, string $keyName, bool $prefixKeyName = tr /** * Drop Primary Key - * - * @param mixed $keyName */ public function dropPrimaryKey(string $table, string $keyName = ''): bool { From d47d1ebbf63c2092618e0c934da7e3d5e0e9cfb0 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 15:28:02 -0700 Subject: [PATCH 06/20] Add dropKey() to postgres Same as oracle - this looks up if key is a constraint --- system/Database/Postgre/Forge.php | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index 6791da6169fa..cb757acebc6e 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -190,4 +190,56 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str return $sql; } + + /** + * Drop Key + * + * @param mixed $prefixKeyName + * + * @return bool + * + * @throws DatabaseException + */ + public function dropKey(string $table, string $keyName, bool $prefixKeyName = true) + { + $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); + + // check if key is a constraint + $sql = "SELECT con.conname + FROM pg_catalog.pg_constraint con + INNER JOIN pg_catalog.pg_class rel + ON rel.oid = con.conrelid + INNER JOIN pg_catalog.pg_namespace nsp + ON nsp.oid = connamespace + WHERE nsp.nspname = '{$this->db->schema}' + AND rel.relname = '" . $this->db->DBPrefix . $table . "' + AND con.conname = '" . trim($keyName, '"') . "'"; + + $constraint = $this->db->query($sql)->getResultArray(); + + $sql = sprintf( + $this->dropIndexStr, + $keyName, + $this->db->escapeIdentifiers($this->db->DBPrefix . $table), + ); + + if (count($constraint) !== 0) { + $sqlString = $this->dropConstraintStr; + $sql = sprintf( + $this->dropConstraintStr, + $this->db->escapeIdentifiers($this->db->DBPrefix . $table), + $keyName, + ); + } + + if ($sql === '') { + if ($this->db->DBDebug) { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + return $this->db->query($sql); + } } From b34c15110f3d0880ce9da5194828da67905c8d40 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 15:55:26 -0700 Subject: [PATCH 07/20] Fix SQLite Table class to use index names --- system/Database/SQLite3/Table.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 2c142086a1d5..e1e0d8aad6e6 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -261,18 +261,18 @@ protected function createTable() // Unique/Index keys if (is_array($this->keys)) { - foreach ($this->keys as $key) { + foreach ($this->keys as $i => $key) { switch ($key['type']) { case 'primary': $this->forge->addPrimaryKey($key['fields']); break; case 'unique': - $this->forge->addUniqueKey($key['fields']); + $this->forge->addUniqueKey($key['fields'], $i); break; case 'index': - $this->forge->addKey($key['fields']); + $this->forge->addKey($key['fields'], false, false, $i); break; } } From 83382bb001e263c97509fe2d24b4900305381c45 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 16:09:36 -0700 Subject: [PATCH 08/20] Add dropKey() to SQLSRV Have to lookup if key is constraint. --- system/Database/SQLSRV/Forge.php | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index b1b54e02c207..8b34eaabd038 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -391,4 +391,50 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str return $sql; } + + /** + * Drop Key + * + * @param mixed $prefixKeyName + * + * @return bool + * + * @throws DatabaseException + */ + public function dropKey(string $table, string $keyName, bool $prefixKeyName = true) + { + $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); + + // check if key is a constraint + $sql = "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS + WHERE TABLE_NAME= '" . $this->db->DBPrefix . $table . "' + AND CONSTRAINT_NAME = '" . trim($keyName, '"') . "'"; + + $constraint = $this->db->query($sql)->getResultArray(); + + $sql = sprintf( + $this->dropIndexStr, + $keyName, + $this->db->escapeIdentifiers($this->db->DBPrefix . $table), + ); + + if (count($constraint) !== 0) { + $sqlString = $this->dropConstraintStr; + $sql = sprintf( + $this->dropConstraintStr, + $this->db->escapeIdentifiers($this->db->DBPrefix . $table), + $keyName, + ); + } + + if ($sql === '') { + if ($this->db->DBDebug) { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + return $this->db->query($sql); + } } From a3faf8fea3a61166053a6587b6bbc57fef129824 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 16:29:05 -0700 Subject: [PATCH 09/20] Add test This test adding custom names but also test removing them by custom names. --- tests/system/Database/Live/ForgeTest.php | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index cc709c0fe5df..1346cfa1e1e0 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -1140,6 +1140,70 @@ public function testCompositeKey() $this->forge->dropTable('forge_test_1', true); } + public function testSetKeyNames() + { + $this->forge->dropTable('forge_test_1', true); + + $this->forge->addField([ + 'id' => [ + 'type' => 'INTEGER', + 'constraint' => 3, + 'auto_increment' => true, + ], + 'code' => [ + 'type' => 'VARCHAR', + 'constraint' => 40, + ], + 'company' => [ + 'type' => 'VARCHAR', + 'constraint' => 40, + ], + 'active' => [ + 'type' => 'INTEGER', + 'constraint' => 1, + ], + ]); + + $pk = 'my_custom_pk'; + $index = 'my_custom_index'; + $uniqueIndex = 'my_custom_unique_index'; + + if ($this->db->DBDriver === 'MySQLi' || $this->db->DBDriver === 'SQLite3') { + $pk = 'PRIMARY'; + } + + $this->forge->addPrimaryKey('id', $pk); + $this->forge->addKey(['code', 'company'], false, false, $index); + $this->forge->addUniqueKey(['code', 'active'], $uniqueIndex); + $this->forge->createTable('forge_test_1', true); + + $keys = $this->db->getIndexData('forge_test_1'); + + // mysql must redefine auto increment which can only exist on a key + if ($this->db->DBDriver === 'MySQLi') { + $id = [ + 'id' => [ + 'name' => 'id', + 'type' => 'INTEGER', + 'constraint' => 3, + ], + ]; + $this->forge->modifyColumn('forge_test_1', $id); + } + + $this->assertSame($keys[$pk]->name, $pk); + $this->assertSame($keys[$index]->name, $index); + $this->assertSame($keys[$uniqueIndex]->name, $uniqueIndex); + + $this->forge->dropPrimaryKey('forge_test_1', $pk); + $this->forge->dropKey('forge_test_1', $index, false); + $this->forge->dropKey('forge_test_1', $uniqueIndex, false); + + $this->assertCount(0, $this->db->getIndexData('forge_test_1')); + + $this->forge->dropTable('forge_test_1', true); + } + public function testDropColumn() { $this->forge->dropTable('forge_test_two', true); From 9eff2f1a91e668f46a58194653e47a32c512a801 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 17:46:53 -0700 Subject: [PATCH 10/20] Fix phpstan --- system/Database/Postgre/Forge.php | 24 ++++++++++++++++++------ system/Database/SQLSRV/Forge.php | 8 +++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index cb757acebc6e..bcad20d5fcf7 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -11,6 +11,8 @@ namespace CodeIgniter\Database\Postgre; +use CodeIgniter\Database\BaseConnection; +use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge as BaseForge; /** @@ -65,6 +67,19 @@ class Forge extends BaseForge */ protected $null = 'NULL'; + /** + * @var Connection + */ + protected $db; + + /** + * Constructor. + */ + public function __construct(BaseConnection $db) + { + parent::__construct($db); + } + /** * CREATE TABLE attributes * @@ -194,8 +209,6 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str /** * Drop Key * - * @param mixed $prefixKeyName - * * @return bool * * @throws DatabaseException @@ -214,7 +227,7 @@ public function dropKey(string $table, string $keyName, bool $prefixKeyName = tr WHERE nsp.nspname = '{$this->db->schema}' AND rel.relname = '" . $this->db->DBPrefix . $table . "' AND con.conname = '" . trim($keyName, '"') . "'"; - + $constraint = $this->db->query($sql)->getResultArray(); $sql = sprintf( @@ -223,9 +236,8 @@ public function dropKey(string $table, string $keyName, bool $prefixKeyName = tr $this->db->escapeIdentifiers($this->db->DBPrefix . $table), ); - if (count($constraint) !== 0) { - $sqlString = $this->dropConstraintStr; - $sql = sprintf( + if ($constraint !== []) { + $sql = sprintf( $this->dropConstraintStr, $this->db->escapeIdentifiers($this->db->DBPrefix . $table), $keyName, diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 8b34eaabd038..a75eff49f90f 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -12,6 +12,7 @@ namespace CodeIgniter\Database\SQLSRV; use CodeIgniter\Database\BaseConnection; +use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge as BaseForge; /** @@ -395,8 +396,6 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str /** * Drop Key * - * @param mixed $prefixKeyName - * * @return bool * * @throws DatabaseException @@ -418,9 +417,8 @@ public function dropKey(string $table, string $keyName, bool $prefixKeyName = tr $this->db->escapeIdentifiers($this->db->DBPrefix . $table), ); - if (count($constraint) !== 0) { - $sqlString = $this->dropConstraintStr; - $sql = sprintf( + if ($constraint !== []) { + $sql = sprintf( $this->dropConstraintStr, $this->db->escapeIdentifiers($this->db->DBPrefix . $table), $keyName, From 3907420c0716fb03bb4c0c061abcd7679105e04d Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sun, 18 Sep 2022 17:54:45 -0700 Subject: [PATCH 11/20] Fix rector --- system/Database/Postgre/Forge.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index bcad20d5fcf7..f2257fca620b 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -11,7 +11,6 @@ namespace CodeIgniter\Database\Postgre; -use CodeIgniter\Database\BaseConnection; use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge as BaseForge; @@ -72,14 +71,6 @@ class Forge extends BaseForge */ protected $db; - /** - * Constructor. - */ - public function __construct(BaseConnection $db) - { - parent::__construct($db); - } - /** * CREATE TABLE attributes * From 5c51465533c263f367f25c8dfcc051eeaeafad0f Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 19 Sep 2022 14:38:12 -0700 Subject: [PATCH 12/20] Refactor dropKey() Get rid of CPD error --- system/Database/Postgre/Forge.php | 44 ++++--------------------------- system/Database/SQLSRV/Forge.php | 44 ++++--------------------------- 2 files changed, 10 insertions(+), 78 deletions(-) diff --git a/system/Database/Postgre/Forge.php b/system/Database/Postgre/Forge.php index f2257fca620b..662fbe666595 100644 --- a/system/Database/Postgre/Forge.php +++ b/system/Database/Postgre/Forge.php @@ -11,7 +11,6 @@ namespace CodeIgniter\Database\Postgre; -use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge as BaseForge; /** @@ -198,51 +197,18 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str } /** - * Drop Key - * - * @return bool - * - * @throws DatabaseException + * Constructs sql to check if key is a constraint. */ - public function dropKey(string $table, string $keyName, bool $prefixKeyName = true) + protected function _dropKeyAsConstraint(string $table, string $constraintName): string { - $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); - - // check if key is a constraint - $sql = "SELECT con.conname + return "SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace WHERE nsp.nspname = '{$this->db->schema}' - AND rel.relname = '" . $this->db->DBPrefix . $table . "' - AND con.conname = '" . trim($keyName, '"') . "'"; - - $constraint = $this->db->query($sql)->getResultArray(); - - $sql = sprintf( - $this->dropIndexStr, - $keyName, - $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - ); - - if ($constraint !== []) { - $sql = sprintf( - $this->dropConstraintStr, - $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - $keyName, - ); - } - - if ($sql === '') { - if ($this->db->DBDebug) { - throw new DatabaseException('This feature is not available for the database you are using.'); - } - - return false; - } - - return $this->db->query($sql); + AND rel.relname = '" . trim($table, '"') . "' + AND con.conname = '" . trim($constraintName, '"') . "'"; } } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index a75eff49f90f..f6079ad3363b 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -12,7 +12,6 @@ namespace CodeIgniter\Database\SQLSRV; use CodeIgniter\Database\BaseConnection; -use CodeIgniter\Database\Exceptions\DatabaseException; use CodeIgniter\Database\Forge as BaseForge; /** @@ -394,45 +393,12 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade): str } /** - * Drop Key - * - * @return bool - * - * @throws DatabaseException + * Constructs sql to check if key is a constraint. */ - public function dropKey(string $table, string $keyName, bool $prefixKeyName = true) + protected function _dropKeyAsConstraint(string $table, string $constraintName): string { - $keyName = $this->db->escapeIdentifiers(($prefixKeyName === true ? $this->db->DBPrefix : '') . $keyName); - - // check if key is a constraint - $sql = "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_NAME= '" . $this->db->DBPrefix . $table . "' - AND CONSTRAINT_NAME = '" . trim($keyName, '"') . "'"; - - $constraint = $this->db->query($sql)->getResultArray(); - - $sql = sprintf( - $this->dropIndexStr, - $keyName, - $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - ); - - if ($constraint !== []) { - $sql = sprintf( - $this->dropConstraintStr, - $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - $keyName, - ); - } - - if ($sql === '') { - if ($this->db->DBDebug) { - throw new DatabaseException('This feature is not available for the database you are using.'); - } - - return false; - } - - return $this->db->query($sql); + return "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS + WHERE TABLE_NAME= '" . trim($table, '"') . "' + AND CONSTRAINT_NAME = '" . trim($constraintName, '"') . "'"; } } From f6fb1689659929ade8d949009f63e101f53c61e7 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 19 Sep 2022 15:53:19 -0700 Subject: [PATCH 13/20] Added documentation --- user_guide_src/source/changelogs/v4.3.0.rst | 12 ++++---- user_guide_src/source/dbmgmt/forge.rst | 33 +++++++++++++++++---- user_guide_src/source/dbmgmt/forge/010.php | 8 ++--- user_guide_src/source/dbmgmt/forge/020.php | 5 ++-- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index 09146748cb21..ade238a16437 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -46,7 +46,6 @@ Others - ``CITestStreamFilter::$buffer = ''`` no longer causes the filter to be registered to listen for streams. Now there is a ``CITestStreamFilter::registration()`` method for this. See :ref:`upgrade-430-stream-filter` for details. - The data structure returned by :ref:`BaseConnection::getForeignKeyData() ` has been changed. -- :php:func:`script_tag()` and :php:func:`safe_mailto()` no longer output ``type="text/javascript"`` in ``