From 44390dd13796028421815dca5422c61a4ef5c92f Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 12:27:11 -0700 Subject: [PATCH 01/41] Normalize foreign Key name Making all DBMS use the same naming convention for foreign keys. --- 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 add01540177a..12bd8b64e0b6 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -284,7 +284,7 @@ protected function _processForeignKeys(string $table): string ]; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); From 7819dcdce1555756a14a6b18ada1d6a7fe1d588a Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:10:47 -0700 Subject: [PATCH 02/41] Update MySql Connection::_foreignKeyData() Changed the data structure to one row per foreign key - column_name and foreign_column_name are an array which will hold composite keys. Also added delete and update rule as well as match option to dataset. --- system/Database/MySQLi/Connection.php | 69 +++++++++++++++++---------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index 9d2681394581..e9471dd31f86 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -482,40 +482,59 @@ protected function _indexData(string $table): array protected function _foreignKeyData(string $table): array { $sql = ' - SELECT - tc.CONSTRAINT_NAME, - tc.TABLE_NAME, - kcu.COLUMN_NAME, - rc.REFERENCED_TABLE_NAME, - kcu.REFERENCED_COLUMN_NAME - FROM information_schema.TABLE_CONSTRAINTS AS tc - INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS AS rc - ON tc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME - AND tc.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA - INNER JOIN information_schema.KEY_COLUMN_USAGE AS kcu - ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME - AND tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA - WHERE - tc.CONSTRAINT_TYPE = ' . $this->escape('FOREIGN KEY') . ' AND - tc.TABLE_SCHEMA = ' . $this->escape($this->database) . ' AND - tc.TABLE_NAME = ' . $this->escape($table); + SELECT + tc.constraint_name, + tc.table_name, + kcu.column_name, + rc.referenced_table_name, + kcu.referenced_column_name, + rc.delete_rule, + rc.update_rule, + rc.match_option + FROM information_schema.table_constraints AS tc + INNER JOIN information_schema.referential_constraints AS rc + ON tc.constraint_name = rc.constraint_name + AND tc.constraint_schema = rc.constraint_schema + INNER JOIN information_schema.key_column_usage AS kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.constraint_schema = kcu.constraint_schema + WHERE + tc.constraint_type = ' . $this->escape('FOREIGN KEY') . ' AND + tc.table_schema = ' . $this->escape($this->database) . ' AND + tc.table_name = ' . $this->escape($table); if (($query = $this->query($sql)) === false) { throw new DatabaseException(lang('Database.failGetForeignKeyData')); } $query = $query->getResultObject(); - $retVal = []; + $ind = []; foreach ($query as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row->CONSTRAINT_NAME; - $obj->table_name = $row->TABLE_NAME; - $obj->column_name = $row->COLUMN_NAME; - $obj->foreign_table_name = $row->REFERENCED_TABLE_NAME; - $obj->foreign_column_name = $row->REFERENCED_COLUMN_NAME; + $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $ind[$row->constraint_name]['table_name'] = $row->table_name; + $ind[$row->constraint_name]['column_name'][] = $row->column_name; + $ind[$row->constraint_name]['foreign_table_name'] = $row->referenced_table_name; + $ind[$row->constraint_name]['foreign_column_name'][] = $row->referenced_column_name; + $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; + $ind[$row->constraint_name]['on_update'] = $row->update_rule; + $ind[$row->constraint_name]['match'] = $row->match_option; + } - $retVal[] = $obj; + $retVal = []; + + foreach ($ind as $row) { + $obj = new stdClass(); + $obj->constraint_name = $row['constraint_name']; + $obj->table_name = $row['table_name']; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$row['constraint_name']] = $obj; } return $retVal; From 27a399b896f9f87cfe112b1f59039b88452fc7cc Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:21:18 -0700 Subject: [PATCH 03/41] Update Postgres Connection::_foreignKeyData() Changed the data structure to one row per foreign key. Added delete and update rule as well as match option to dataset. Fixed table join condition ordinal_position. --- system/Database/Postgre/Connection.php | 60 +++++++++++++++++--------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 995719a33c0d..211a2fc0a392 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -326,35 +326,55 @@ protected function _indexData(string $table): array */ protected function _foreignKeyData(string $table): array { - $sql = 'SELECT - tc.constraint_name, tc.table_name, kcu.column_name, - ccu.table_name AS foreign_table_name, - ccu.column_name AS foreign_column_name - FROM information_schema.table_constraints AS tc - JOIN information_schema.key_column_usage AS kcu - ON tc.constraint_name = kcu.constraint_name - JOIN information_schema.constraint_column_usage AS ccu - ON ccu.constraint_name = tc.constraint_name - WHERE constraint_type = ' . $this->escape('FOREIGN KEY') . ' AND - tc.table_name = ' . $this->escape($table); + $sql = 'SELECT c.constraint_name, + x.table_name, + x.column_name, + y.table_name as foreign_table_name, + y.column_name as foreign_column_name, + c.delete_rule, + c.update_rule, + c.match_option + FROM information_schema.referential_constraints c + JOIN information_schema.key_column_usage x + on x.constraint_name = c.constraint_name + JOIN information_schema.key_column_usage y + on y.ordinal_position = x.position_in_unique_constraint + and y.constraint_name = c.unique_constraint_name + WHERE x.table_name = ' . $this->escape($table) . + 'order by c.constraint_name, x.ordinal_position'; if (($query = $this->query($sql)) === false) { throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); - $retVal = []; + $query = $query->getResultObject(); + $ind = []; foreach ($query as $row) { - $obj = new stdClass(); + $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $ind[$row->constraint_name]['table_name'] = $row->table_name; + $ind[$row->constraint_name]['column_name'][] = $row->column_name; + $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; + $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; + $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; + $ind[$row->constraint_name]['on_update'] = $row->update_rule; + $ind[$row->constraint_name]['match'] = $row->match_option; + } - $obj->constraint_name = $row->constraint_name; - $obj->table_name = $row->table_name; - $obj->column_name = $row->column_name; - $obj->foreign_table_name = $row->foreign_table_name; - $obj->foreign_column_name = $row->foreign_column_name; + $retVal = []; - $retVal[] = $obj; + foreach ($ind as $row) { + $obj = new stdClass(); + $obj->constraint_name = $row['constraint_name']; + $obj->table_name = $row['table_name']; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$row['constraint_name']] = $obj; } return $retVal; From 44e152f0cbdfc8e285a66d98a9a2ecaf21e99508 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:30:37 -0700 Subject: [PATCH 04/41] Update Sqlite Connection::_foreignKeyData() Changed the data structure to one row per foreign key. Also added delete and update rule as well as match option to dataset. Sqlite doesn't give the index name so the name is generated in the same manner as in Forge --- system/Database/SQLite3/Connection.php | 43 +++++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index cdbd5c87218f..490af80b9e4b 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -333,26 +333,39 @@ protected function _foreignKeyData(string $table): array return []; } - $tables = $this->listTables(); + $retVal = []; - if (empty($tables)) { - return []; - } + $query = $this->query("PRAGMA foreign_key_list({$table})")->getResult(); - $retVal = []; + $indexes = []; - foreach ($tables as $table) { - $query = $this->query("PRAGMA foreign_key_list({$table})")->getResult(); + foreach ($query as $row) { + $indexes[$row->id]['constraint_name'][] = $row->from; + $indexes[$row->id]['foreign_table_name'] = $row->table; + $indexes[$row->id]['column_name'][] = $row->from; + $indexes[$row->id]['foreign_column_name'][] = $row->to; + $indexes[$row->id]['on_delete'] = $row->on_delete; + $indexes[$row->id]['on_update'] = $row->on_update; + $indexes[$row->id]['match'] = $row->match; + } - foreach ($query as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row->from . ' to ' . $row->table . '.' . $row->to; - $obj->table_name = $table; - $obj->foreign_table_name = $row->table; - $obj->sequence = $row->seq; + $retVal = []; - $retVal[] = $obj; - } + // now we can build index name and convert to object + foreach ($indexes as $row) { + $name = $table . '_' . implode('_', $row['constraint_name']) . '_foreign'; + + $obj = new stdClass(); + $obj->constraint_name = $name; + $obj->table_name = $table; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$row['constraint_name']] = $obj; } return $retVal; From 3d312e2d3e88075b6977c8f6478576597303d948 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:36:05 -0700 Subject: [PATCH 05/41] Update SQLSRV Connection::_foreignKeyData() Changed the data structure to one row per foreign key. Also added delete and update rule as well as match option to dataset. --- system/Database/SQLSRV/Connection.php | 64 ++++++++++++++++----------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 1520543261c8..1018b782d95d 100755 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -273,40 +273,54 @@ protected function _indexData(string $table): array */ protected function _foreignKeyData(string $table): array { - $sql = 'SELECT ' - . 'f.name as constraint_name, ' - . 'OBJECT_NAME (f.parent_object_id) as table_name, ' - . 'COL_NAME(fc.parent_object_id,fc.parent_column_id) column_name, ' - . 'OBJECT_NAME(f.referenced_object_id) foreign_table_name, ' - . 'COL_NAME(fc.referenced_object_id,fc.referenced_column_id) foreign_column_name ' - . 'FROM ' - . 'sys.foreign_keys AS f ' - . 'INNER JOIN ' - . 'sys.foreign_key_columns AS fc ' - . 'ON f.OBJECT_ID = fc.constraint_object_id ' - . 'INNER JOIN ' - . 'sys.tables t ' - . 'ON t.OBJECT_ID = fc.referenced_object_id ' - . 'WHERE ' - . 'OBJECT_NAME (f.parent_object_id) = ' . $this->escape($table); + $sql = 'SELECT + f.name as constraint_name, + OBJECT_NAME (f.parent_object_id) as table_name, + COL_NAME(fc.parent_object_id,fc.parent_column_id) column_name, + OBJECT_NAME(f.referenced_object_id) foreign_table_name, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) foreign_column_name, + rc.delete_rule, + rc.update_rule, + rc.match_option + FROM + sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_id + INNER JOIN sys.tables t ON t.OBJECT_ID = fc.referenced_object_id + INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc ON rc.CONSTRAINT_NAME = f.name + WHERE OBJECT_NAME (f.parent_object_id) = ' . $this->escape($table); if (($query = $this->query($sql)) === false) { throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); - $retVal = []; + $query = $query->getResultObject(); + $ind = []; foreach ($query as $row) { - $obj = new stdClass(); + $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $ind[$row->constraint_name]['table_name'] = $row->table_name; + $ind[$row->constraint_name]['column_name'][] = $row->column_name; + $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; + $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; + $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; + $ind[$row->constraint_name]['on_update'] = $row->update_rule; + $ind[$row->constraint_name]['match'] = $row->match_option; + } - $obj->constraint_name = $row->constraint_name; - $obj->table_name = $row->table_name; - $obj->column_name = $row->column_name; - $obj->foreign_table_name = $row->foreign_table_name; - $obj->foreign_column_name = $row->foreign_column_name; + $retVal = []; - $retVal[] = $obj; + foreach ($ind as $row) { + $obj = new stdClass(); + $obj->constraint_name = $row['constraint_name']; + $obj->table_name = $row['table_name']; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$row['constraint_name']] = $obj; } return $retVal; From 913759edd94965aebb75833ab2b2a6d911b90d85 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:43:06 -0700 Subject: [PATCH 06/41] Update Oracle Connection::_foreignKeyData() Changed the data structure to one row per foreign key. Also added delete and update rule as well as match option to dataset. Also fixed join condition accu.position = acc.position to prevent duplicate rows being output. --- system/Database/OCI8/Connection.php | 65 ++++++++++++++++++----------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 854a36ab3929..7dff23fe0cee 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -384,23 +384,23 @@ protected function _indexData(string $table): array protected function _foreignKeyData(string $table): array { $sql = 'SELECT - acc.constraint_name, - acc.table_name, - acc.column_name, - ccu.table_name foreign_table_name, - accu.column_name foreign_column_name - FROM all_cons_columns acc - JOIN all_constraints ac - ON acc.owner = ac.owner - AND acc.constraint_name = ac.constraint_name - JOIN all_constraints ccu - ON ac.r_owner = ccu.owner - AND ac.r_constraint_name = ccu.constraint_name - JOIN all_cons_columns accu - ON accu.constraint_name = ccu.constraint_name - AND accu.table_name = ccu.table_name - WHERE ac.constraint_type = ' . $this->escape('R') . ' - AND acc.table_name = ' . $this->escape($table); + acc.constraint_name, + acc.table_name, + acc.column_name, + ccu.table_name foreign_table_name, + accu.column_name foreign_column_name, + ac.delete_rule + FROM all_cons_columns acc + JOIN all_constraints ac ON acc.owner = ac.owner + AND acc.constraint_name = ac.constraint_name + JOIN all_constraints ccu ON ac.r_owner = ccu.owner + AND ac.r_constraint_name = ccu.constraint_name + JOIN all_cons_columns accu ON accu.constraint_name = ccu.constraint_name + AND accu.position = acc.position + AND accu.table_name = ccu.table_name + WHERE ac.constraint_type = ' . $this->escape('R') . ' + AND acc.table_name = ' . $this->escape($table); + $query = $this->query($sql); if ($query === false) { @@ -408,16 +408,33 @@ protected function _foreignKeyData(string $table): array } $query = $query->getResultObject(); - $retVal = []; + $ind = []; foreach ($query as $row) { + $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $ind[$row->constraint_name]['table_name'] = $row->table_name; + $ind[$row->constraint_name]['column_name'][] = $row->column_name; + $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; + $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; + $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; + $ind[$row->constraint_name]['on_update'] = null; + $ind[$row->constraint_name]['match'] = null; + } + + $retVal = []; + + foreach ($ind as $row) { $obj = new stdClass(); - $obj->constraint_name = $row->CONSTRAINT_NAME; - $obj->table_name = $row->TABLE_NAME; - $obj->column_name = $row->COLUMN_NAME; - $obj->foreign_table_name = $row->FOREIGN_TABLE_NAME; - $obj->foreign_column_name = $row->FOREIGN_COLUMN_NAME; - $retVal[] = $obj; + $obj->constraint_name = $row['constraint_name']; + $obj->table_name = $row['table_name']; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$row['constraint_name']] = $obj; } return $retVal; From 1f15091f2f37d5951270cdca3753b674c9d81d4b Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:47:55 -0700 Subject: [PATCH 07/41] Fix Sqlite Array Index Error --- system/Database/SQLite3/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 490af80b9e4b..1f0dd2b04497 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -365,7 +365,7 @@ protected function _foreignKeyData(string $table): array $obj->on_update = $row['on_update']; $obj->match = $row['match']; - $retVal[$row['constraint_name']] = $obj; + $retVal[$name] = $obj; } return $retVal; From 087a0eda40783bdc353faf9b829fbd2ab261b7f9 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 13:59:02 -0700 Subject: [PATCH 08/41] Fix Sqlite Table to work with new data structure _foreignKeyData() Small change in logic required to use new naming convention. --- system/Database/SQLite3/Table.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/system/Database/SQLite3/Table.php b/system/Database/SQLite3/Table.php index 0f6911b62c30..2c142086a1d5 100644 --- a/system/Database/SQLite3/Table.php +++ b/system/Database/SQLite3/Table.php @@ -214,23 +214,14 @@ public function dropPrimaryKey(): Table * * @return Table */ - public function dropForeignKey(string $column) + public function dropForeignKey(string $foreignName) { if (empty($this->foreignKeys)) { return $this; } - for ($i = 0; $i < count($this->foreignKeys); $i++) { - if ($this->foreignKeys[$i]->table_name !== $this->tableName) { - continue; - } - - // The column name should be the first thing in the constraint name - if (strpos($this->foreignKeys[$i]->constraint_name, $column) !== 0) { - continue; - } - - unset($this->foreignKeys[$i]); + if (isset($this->foreignKeys[$foreignName])) { + unset($this->foreignKeys[$foreignName]); } return $this; From 3423949262476561d650f75146a8bb22cc1757b3 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 14:00:12 -0700 Subject: [PATCH 09/41] Fix AlterTableTest - use new data structure from _foreignKeyData() --- tests/system/Database/Live/SQLite/AlterTableTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 0d3da18d1a81..61696ec9d573 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -224,11 +224,11 @@ public function testDropForeignKeySuccess() $this->createTable('aliens'); $keys = $this->db->getForeignKeyData('aliens'); - $this->assertSame('key_id to aliens_fk.id', $keys[0]->constraint_name); + $this->assertSame($this->db->DBPrefix . 'aliens_key_id_foreign', $keys[$this->db->DBPrefix . 'aliens_key_id_foreign']->constraint_name); $result = $this->table ->fromTable('aliens') - ->dropForeignKey('key_id') + ->dropForeignKey('aliens_key_id_foreign') ->run(); $this->assertTrue($result); From 1fc411d3188d8b8c0b89e5fc593f6eb2aaaed9bb Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 15:14:49 -0700 Subject: [PATCH 10/41] Fix Oracle Connection::_foreignKeyData() letter case --- system/Database/OCI8/Connection.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 7dff23fe0cee..923d8c1f7fd9 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -411,14 +411,14 @@ protected function _foreignKeyData(string $table): array $ind = []; foreach ($query as $row) { - $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $ind[$row->constraint_name]['table_name'] = $row->table_name; - $ind[$row->constraint_name]['column_name'][] = $row->column_name; - $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; - $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; - $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; - $ind[$row->constraint_name]['on_update'] = null; - $ind[$row->constraint_name]['match'] = null; + $ind[$row->CONSTRAINT_NAME]['constraint_name'] = $row->CONSTRAINT_NAME; + $ind[$row->CONSTRAINT_NAME]['table_name'] = $row->TABLE_NAME; + $ind[$row->CONSTRAINT_NAME]['column_name'][] = $row->COLUMN_NAME; + $ind[$row->CONSTRAINT_NAME]['foreign_table_name'] = $row->FOREIGN_TABLE_NAME; + $ind[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->FOREIGN_COLUMN_NAME; + $ind[$row->CONSTRAINT_NAME]['on_delete'] = $row->DELETE_RULE; + $ind[$row->CONSTRAINT_NAME]['on_update'] = null; + $ind[$row->CONSTRAINT_NAME]['match'] = null; } $retVal = []; From a66dbe88d84b3644b7c969d9bfa490a41b2a3429 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 15:43:15 -0700 Subject: [PATCH 11/41] Update ForgeTest to work with new data structure of Connection::_foreignKeyData() This greatly simplified the Forge tests. --- tests/system/Database/Live/ForgeTest.php | 121 +++++++++-------------- 1 file changed, 44 insertions(+), 77 deletions(-) diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 0dd6ae86e89a..304dde462a3f 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -34,8 +34,15 @@ final class ForgeTest extends CIUnitTestCase protected function setUp(): void { - parent::setUp(); $this->forge = Database::forge($this->DBGroup); + + // when running locally if one of these tables isn't dropped it may cause error + $this->forge->dropTable('forge_test_invoices', true); + $this->forge->dropTable('forge_test_inv', true); + $this->forge->dropTable('forge_test_users', true); + $this->forge->dropTable('actions', true); + + parent::setUp(); } public function testCreateDatabase() @@ -433,6 +440,7 @@ public function testDropTableWithEmptyName() public function testForeignKey() { + $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); $attributes = []; @@ -472,28 +480,16 @@ public function testForeignKey() $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE'); $tableName = 'forge_test_invoices'; - if ($this->db->DBDriver === 'OCI8') { - $tableName = 'forge_test_inv'; - } $this->forge->createTable($tableName, true, $attributes); $foreignKeyData = $this->db->getForeignKeyData($tableName); - if ($this->db->DBDriver === 'SQLite3') { - $this->assertSame($foreignKeyData[0]->constraint_name, 'users_id to db_forge_test_users.id'); - $this->assertSame($foreignKeyData[0]->sequence, 0); - } elseif ($this->db->DBDriver === 'OCI8') { - $this->assertSame($foreignKeyData[0]->constraint_name, $this->db->DBPrefix . 'forge_test_inv_users_id_fk'); - $this->assertSame($foreignKeyData[0]->column_name, 'users_id'); - $this->assertSame($foreignKeyData[0]->foreign_column_name, 'id'); - } else { - $this->assertSame($foreignKeyData[0]->constraint_name, $this->db->DBPrefix . 'forge_test_invoices_users_id_foreign'); - $this->assertSame($foreignKeyData[0]->column_name, 'users_id'); - $this->assertSame($foreignKeyData[0]->foreign_column_name, 'id'); - } - $this->assertSame($foreignKeyData[0]->table_name, $this->db->DBPrefix . $tableName); - $this->assertSame($foreignKeyData[0]->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->constraint_name, $this->db->DBPrefix . 'forge_test_invoices_users_id_foreign'); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->column_name, ['users_id']); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->foreign_column_name, ['id']); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->table_name, $this->db->DBPrefix . $tableName); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); $this->forge->dropTable($tableName, true); $this->forge->dropTable('forge_test_users', true); @@ -524,13 +520,13 @@ public function testForeignKeyAddingWithStringFields() ->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE') ->createTable('forge_test_invoices', true, $attributes); - $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices')[0]; + $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_foreign', $foreignKeyData->constraint_name); - $this->assertSame('users_id', $foreignKeyData->column_name); - $this->assertSame('id', $foreignKeyData->foreign_column_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData->foreign_table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_foreign', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->constraint_name); + $this->assertSame(['users_id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->column_name); + $this->assertSame(['id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->foreign_column_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->foreign_table_name); $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); @@ -541,6 +537,9 @@ public function testForeignKeyAddingWithStringFields() */ public function testCompositeForeignKey() { + $this->forge->dropTable('forge_test_invoices', true); + $this->forge->dropTable('forge_test_users', true); + $attributes = []; if ($this->db->DBDriver === 'MySQLi') { @@ -564,10 +563,7 @@ public function testCompositeForeignKey() $this->forge->addPrimaryKey(['id', 'second_id']); $this->forge->createTable('forge_test_users', true, $attributes); - $forgeTestInvoicesTableName = 'forge_test_invoices'; - $userIdColumnName = 'users_id'; - $userSecondIdColumnName = 'users_second_id'; - $fields = [ + $fields = [ 'id' => [ 'type' => 'INTEGER', 'constraint' => 11, @@ -576,58 +572,32 @@ public function testCompositeForeignKey() 'type' => 'VARCHAR', 'constraint' => 255, ], - ]; - - if ($this->db->DBDriver === 'OCI8') { - $userIdColumnName = 'uid'; - $userSecondIdColumnName = 'usid'; - $forgeTestInvoicesTableName = 'forge_test_inv'; - } - - $fields[$userIdColumnName] = [ - 'type' => 'INTEGER', - 'constraint' => 11, - ]; - - $fields[$userSecondIdColumnName] = [ - 'type' => 'VARCHAR', - 'constraint' => 50, + 'users_id' => [ + 'type' => 'INTEGER', + 'constraint' => 11, + ], + 'users_second_id' => [ + 'type' => 'VARCHAR', + 'constraint' => 50, + ], ]; $this->forge->addField($fields); $this->forge->addPrimaryKey('id'); - $this->forge->addForeignKey([$userIdColumnName, $userSecondIdColumnName], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE'); + $this->forge->addForeignKey(['users_id', 'users_second_id'], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE'); - $this->forge->createTable($forgeTestInvoicesTableName, true, $attributes); + $this->forge->createTable('forge_test_invoices', true, $attributes); - $foreignKeyData = $this->db->getForeignKeyData($forgeTestInvoicesTableName); + $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); - if ($this->db->DBDriver === 'SQLite3') { - $this->assertSame('users_id to db_forge_test_users.id', $foreignKeyData[0]->constraint_name); - $this->assertSame(0, $foreignKeyData[0]->sequence); - $this->assertSame('users_second_id to db_forge_test_users.second_id', $foreignKeyData[1]->constraint_name); - $this->assertSame(1, $foreignKeyData[1]->sequence); - } elseif ($this->db->DBDriver === 'OCI8') { - $haystack = [$userIdColumnName, $userSecondIdColumnName]; - $this->assertSame($this->db->DBPrefix . 'forge_test_inv_uid_usid_fk', $foreignKeyData[0]->constraint_name); - $this->assertContains($foreignKeyData[0]->column_name, $haystack); - - $secondIdKey = 1; - $this->assertSame($this->db->DBPrefix . 'forge_test_inv_uid_usid_fk', $foreignKeyData[$secondIdKey]->constraint_name); - $this->assertContains($foreignKeyData[$secondIdKey]->column_name, $haystack); - } else { - $haystack = [$userIdColumnName, $userSecondIdColumnName]; - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign', $foreignKeyData[0]->constraint_name); - $this->assertContains($foreignKeyData[0]->column_name, $haystack); + $haystack = ['users_id', 'users_second_id']; + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->constraint_name); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->column_name, $haystack); - $secondIdKey = $this->db->DBDriver === 'Postgre' ? 2 : 1; - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign', $foreignKeyData[$secondIdKey]->constraint_name); - $this->assertContains($foreignKeyData[$secondIdKey]->column_name, $haystack); - } - $this->assertSame($this->db->DBPrefix . $forgeTestInvoicesTableName, $foreignKeyData[0]->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[0]->foreign_table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->foreign_table_name); - $this->forge->dropTable($forgeTestInvoicesTableName, true); + $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); } @@ -732,6 +702,7 @@ public function testForeignKeyFieldNotExistException() public function testDropForeignKey() { + $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); $attributes = []; @@ -772,10 +743,6 @@ public function testDropForeignKey() $tableName = 'forge_test_invoices'; $foreignKeyName = 'forge_test_invoices_users_id_foreign'; - if ($this->db->DBDriver === 'OCI8') { - $tableName = 'forge_test_inv'; - $foreignKeyName = 'forge_test_inv_users_id_fk'; - } $this->forge->createTable($tableName, true, $attributes); @@ -1034,8 +1001,8 @@ public function testAddFields() ], ]; - // Sequence id may change - $this->assertMatchesRegularExpression('/"ORACLE"."ISEQ\\$\\$_\d+".nextval/', $fieldsData[0]->default); + // Sequence id may change - MAY USE "SYSTEM" instead of "ORACLE" + $this->assertMatchesRegularExpression('/"(ORACLE|SYSTEM)"."ISEQ\\$\\$_\d+".nextval/', $fieldsData[0]->default); $expected[0]['default'] = $fieldsData[0]->default; } else { $this->fail(sprintf('DB driver "%s" is not supported.', $this->db->DBDriver)); From bd1e18c8f39455de29d5638ccb6344a04810c259 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 1 Sep 2022 15:53:45 -0700 Subject: [PATCH 12/41] Added Change Log Note --- user_guide_src/source/changelogs/v4.3.0.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index 6a794ec5b157..4f344c0c3b2c 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -12,6 +12,9 @@ Release Date: Unreleased BREAKING ******** +- The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. +- The naming of foreign keys in has changed in all DBMS except OCI8. The previous suffix of ``_foreign`` has changed to ``_fk``. + Behavior Changes ================ @@ -117,6 +120,7 @@ Database - ``BaseConnection::escape()`` now excludes the ``RawSql`` data type. This allows passing SQL strings into data. - The new method ``Forge::dropPrimaryKey()`` allows dropping the primary key on a table. See :ref:`dropping-a-primary-key`. - Improved the SQL structure for ``Builder::updateBatch()``. See :ref:`update-batch` for the details. +- Improved data returned by ``BaseConnection::getForeignKeyData()``. All DBMS return the same structure and use the same naming conventions. Model ===== From 9584fc7a9401cf65ede4ac8dc9a7a8533bc61071 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 13:51:58 -0700 Subject: [PATCH 13/41] Fix variable name $ind to $indexes --- system/Database/MySQLi/Connection.php | 20 ++++++++++---------- system/Database/OCI8/Connection.php | 20 ++++++++++---------- system/Database/Postgre/Connection.php | 20 ++++++++++---------- system/Database/SQLSRV/Connection.php | 20 ++++++++++---------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index e9471dd31f86..c675d75bb90d 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -508,22 +508,22 @@ protected function _foreignKeyData(string $table): array } $query = $query->getResultObject(); - $ind = []; + $indexes = []; foreach ($query as $row) { - $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $ind[$row->constraint_name]['table_name'] = $row->table_name; - $ind[$row->constraint_name]['column_name'][] = $row->column_name; - $ind[$row->constraint_name]['foreign_table_name'] = $row->referenced_table_name; - $ind[$row->constraint_name]['foreign_column_name'][] = $row->referenced_column_name; - $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; - $ind[$row->constraint_name]['on_update'] = $row->update_rule; - $ind[$row->constraint_name]['match'] = $row->match_option; + $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $indexes[$row->constraint_name]['table_name'] = $row->table_name; + $indexes[$row->constraint_name]['column_name'][] = $row->column_name; + $indexes[$row->constraint_name]['foreign_table_name'] = $row->referenced_table_name; + $indexes[$row->constraint_name]['foreign_column_name'][] = $row->referenced_column_name; + $indexes[$row->constraint_name]['on_delete'] = $row->delete_rule; + $indexes[$row->constraint_name]['on_update'] = $row->update_rule; + $indexes[$row->constraint_name]['match'] = $row->match_option; } $retVal = []; - foreach ($ind as $row) { + foreach ($indexes as $row) { $obj = new stdClass(); $obj->constraint_name = $row['constraint_name']; $obj->table_name = $row['table_name']; diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index 923d8c1f7fd9..cd090bf1cf63 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -408,22 +408,22 @@ protected function _foreignKeyData(string $table): array } $query = $query->getResultObject(); - $ind = []; + $indexes = []; foreach ($query as $row) { - $ind[$row->CONSTRAINT_NAME]['constraint_name'] = $row->CONSTRAINT_NAME; - $ind[$row->CONSTRAINT_NAME]['table_name'] = $row->TABLE_NAME; - $ind[$row->CONSTRAINT_NAME]['column_name'][] = $row->COLUMN_NAME; - $ind[$row->CONSTRAINT_NAME]['foreign_table_name'] = $row->FOREIGN_TABLE_NAME; - $ind[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->FOREIGN_COLUMN_NAME; - $ind[$row->CONSTRAINT_NAME]['on_delete'] = $row->DELETE_RULE; - $ind[$row->CONSTRAINT_NAME]['on_update'] = null; - $ind[$row->CONSTRAINT_NAME]['match'] = null; + $indexes[$row->CONSTRAINT_NAME]['constraint_name'] = $row->CONSTRAINT_NAME; + $indexes[$row->CONSTRAINT_NAME]['table_name'] = $row->TABLE_NAME; + $indexes[$row->CONSTRAINT_NAME]['column_name'][] = $row->COLUMN_NAME; + $indexes[$row->CONSTRAINT_NAME]['foreign_table_name'] = $row->FOREIGN_TABLE_NAME; + $indexes[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->FOREIGN_COLUMN_NAME; + $indexes[$row->CONSTRAINT_NAME]['on_delete'] = $row->DELETE_RULE; + $indexes[$row->CONSTRAINT_NAME]['on_update'] = null; + $indexes[$row->CONSTRAINT_NAME]['match'] = null; } $retVal = []; - foreach ($ind as $row) { + foreach ($indexes as $row) { $obj = new stdClass(); $obj->constraint_name = $row['constraint_name']; $obj->table_name = $row['table_name']; diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index 211a2fc0a392..f8dc874280a3 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -348,22 +348,22 @@ protected function _foreignKeyData(string $table): array } $query = $query->getResultObject(); - $ind = []; + $indexes = []; foreach ($query as $row) { - $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $ind[$row->constraint_name]['table_name'] = $row->table_name; - $ind[$row->constraint_name]['column_name'][] = $row->column_name; - $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; - $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; - $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; - $ind[$row->constraint_name]['on_update'] = $row->update_rule; - $ind[$row->constraint_name]['match'] = $row->match_option; + $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $indexes[$row->constraint_name]['table_name'] = $row->table_name; + $indexes[$row->constraint_name]['column_name'][] = $row->column_name; + $indexes[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; + $indexes[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; + $indexes[$row->constraint_name]['on_delete'] = $row->delete_rule; + $indexes[$row->constraint_name]['on_update'] = $row->update_rule; + $indexes[$row->constraint_name]['match'] = $row->match_option; } $retVal = []; - foreach ($ind as $row) { + foreach ($indexes as $row) { $obj = new stdClass(); $obj->constraint_name = $row['constraint_name']; $obj->table_name = $row['table_name']; diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 1018b782d95d..52a4ee47c598 100755 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -294,22 +294,22 @@ protected function _foreignKeyData(string $table): array } $query = $query->getResultObject(); - $ind = []; + $indexes = []; foreach ($query as $row) { - $ind[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $ind[$row->constraint_name]['table_name'] = $row->table_name; - $ind[$row->constraint_name]['column_name'][] = $row->column_name; - $ind[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; - $ind[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; - $ind[$row->constraint_name]['on_delete'] = $row->delete_rule; - $ind[$row->constraint_name]['on_update'] = $row->update_rule; - $ind[$row->constraint_name]['match'] = $row->match_option; + $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; + $indexes[$row->constraint_name]['table_name'] = $row->table_name; + $indexes[$row->constraint_name]['column_name'][] = $row->column_name; + $indexes[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; + $indexes[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; + $indexes[$row->constraint_name]['on_delete'] = $row->delete_rule; + $indexes[$row->constraint_name]['on_update'] = $row->update_rule; + $indexes[$row->constraint_name]['match'] = $row->match_option; } $retVal = []; - foreach ($ind as $row) { + foreach ($indexes as $row) { $obj = new stdClass(); $obj->constraint_name = $row['constraint_name']; $obj->table_name = $row['table_name']; From f71705e60f286c96e0a59049ba582d40d20a59cc Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 14:34:49 -0700 Subject: [PATCH 14/41] Refactor _foreignKeyData() by adding BaseConnection::foreignKeyDataToObjects() Fix duplicate code by using common function to convert array to object. --- system/Database/BaseConnection.php | 31 ++++++++++++++++++++++++++ system/Database/MySQLi/Connection.php | 20 ++--------------- system/Database/OCI8/Connection.php | 20 ++--------------- system/Database/Postgre/Connection.php | 24 ++++---------------- system/Database/SQLSRV/Connection.php | 22 +++--------------- system/Database/SQLite3/Connection.php | 29 ++++-------------------- 6 files changed, 46 insertions(+), 100 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index a7a8b64acb28..2fd8d8e5b56f 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1541,6 +1541,37 @@ public function getForeignKeyData(string $table) return $this->_foreignKeyData($this->protectIdentifiers($table, true, false, false)); } + /** + * Converts array of arrays generated by _foreignKeyData() to array of objects + */ + protected function foreignKeyDataToObjects(array $data): array + { + $retVal = []; + + foreach ($data as $row) { + $name = $row['constraint_name']; + + // for sqlite generate name + if ($name === null) { + $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_foreign'; + } + + $obj = new stdClass(); + $obj->constraint_name = $name; + $obj->table_name = $row['table_name']; + $obj->column_name = $row['column_name']; + $obj->foreign_table_name = $row['foreign_table_name']; + $obj->foreign_column_name = $row['foreign_column_name']; + $obj->on_delete = $row['on_delete']; + $obj->on_update = $row['on_update']; + $obj->match = $row['match']; + + $retVal[$name] = $obj; + } + + return $retVal; + } + /** * Disables foreign key checks temporarily. */ diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index c675d75bb90d..be2873ccb98e 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -506,8 +506,8 @@ protected function _foreignKeyData(string $table): array if (($query = $this->query($sql)) === false) { throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); + $query = $query->getResultObject(); $indexes = []; foreach ($query as $row) { @@ -521,23 +521,7 @@ protected function _foreignKeyData(string $table): array $indexes[$row->constraint_name]['match'] = $row->match_option; } - $retVal = []; - - foreach ($indexes as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row['constraint_name']; - $obj->table_name = $row['table_name']; - $obj->column_name = $row['column_name']; - $obj->foreign_table_name = $row['foreign_table_name']; - $obj->foreign_column_name = $row['foreign_column_name']; - $obj->on_delete = $row['on_delete']; - $obj->on_update = $row['on_update']; - $obj->match = $row['match']; - - $retVal[$row['constraint_name']] = $obj; - } - - return $retVal; + return $this->foreignKeyDataToObjects($indexes); } /** diff --git a/system/Database/OCI8/Connection.php b/system/Database/OCI8/Connection.php index cd090bf1cf63..73c53b2e107c 100644 --- a/system/Database/OCI8/Connection.php +++ b/system/Database/OCI8/Connection.php @@ -406,8 +406,8 @@ protected function _foreignKeyData(string $table): array if ($query === false) { throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); + $query = $query->getResultObject(); $indexes = []; foreach ($query as $row) { @@ -421,23 +421,7 @@ protected function _foreignKeyData(string $table): array $indexes[$row->CONSTRAINT_NAME]['match'] = null; } - $retVal = []; - - foreach ($indexes as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row['constraint_name']; - $obj->table_name = $row['table_name']; - $obj->column_name = $row['column_name']; - $obj->foreign_table_name = $row['foreign_table_name']; - $obj->foreign_column_name = $row['foreign_column_name']; - $obj->on_delete = $row['on_delete']; - $obj->on_update = $row['on_update']; - $obj->match = $row['match']; - - $retVal[$row['constraint_name']] = $obj; - } - - return $retVal; + return $this->foreignKeyDataToObjects($indexes); } /** diff --git a/system/Database/Postgre/Connection.php b/system/Database/Postgre/Connection.php index f8dc874280a3..cd6ada29322e 100644 --- a/system/Database/Postgre/Connection.php +++ b/system/Database/Postgre/Connection.php @@ -347,12 +347,12 @@ protected function _foreignKeyData(string $table): array throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); - $indexes = []; + $query = $query->getResultObject(); + $indexes = []; foreach ($query as $row) { $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $indexes[$row->constraint_name]['table_name'] = $row->table_name; + $indexes[$row->constraint_name]['table_name'] = $table; $indexes[$row->constraint_name]['column_name'][] = $row->column_name; $indexes[$row->constraint_name]['foreign_table_name'] = $row->foreign_table_name; $indexes[$row->constraint_name]['foreign_column_name'][] = $row->foreign_column_name; @@ -361,23 +361,7 @@ protected function _foreignKeyData(string $table): array $indexes[$row->constraint_name]['match'] = $row->match_option; } - $retVal = []; - - foreach ($indexes as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row['constraint_name']; - $obj->table_name = $row['table_name']; - $obj->column_name = $row['column_name']; - $obj->foreign_table_name = $row['foreign_table_name']; - $obj->foreign_column_name = $row['foreign_column_name']; - $obj->on_delete = $row['on_delete']; - $obj->on_update = $row['on_update']; - $obj->match = $row['match']; - - $retVal[$row['constraint_name']] = $obj; - } - - return $retVal; + return $this->foreignKeyDataToObjects($indexes); } /** diff --git a/system/Database/SQLSRV/Connection.php b/system/Database/SQLSRV/Connection.php index 52a4ee47c598..134421610e1e 100755 --- a/system/Database/SQLSRV/Connection.php +++ b/system/Database/SQLSRV/Connection.php @@ -293,8 +293,8 @@ protected function _foreignKeyData(string $table): array throw new DatabaseException(lang('Database.failGetForeignKeyData')); } - $query = $query->getResultObject(); - $indexes = []; + $query = $query->getResultObject(); + $indexes = []; foreach ($query as $row) { $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; @@ -307,23 +307,7 @@ protected function _foreignKeyData(string $table): array $indexes[$row->constraint_name]['match'] = $row->match_option; } - $retVal = []; - - foreach ($indexes as $row) { - $obj = new stdClass(); - $obj->constraint_name = $row['constraint_name']; - $obj->table_name = $row['table_name']; - $obj->column_name = $row['column_name']; - $obj->foreign_table_name = $row['foreign_table_name']; - $obj->foreign_column_name = $row['foreign_column_name']; - $obj->on_delete = $row['on_delete']; - $obj->on_update = $row['on_update']; - $obj->match = $row['match']; - - $retVal[$row['constraint_name']] = $obj; - } - - return $retVal; + return $this->foreignKeyDataToObjects($indexes); } /** diff --git a/system/Database/SQLite3/Connection.php b/system/Database/SQLite3/Connection.php index 1f0dd2b04497..37e079411efe 100644 --- a/system/Database/SQLite3/Connection.php +++ b/system/Database/SQLite3/Connection.php @@ -333,14 +333,12 @@ protected function _foreignKeyData(string $table): array return []; } - $retVal = []; - - $query = $this->query("PRAGMA foreign_key_list({$table})")->getResult(); - + $query = $this->query("PRAGMA foreign_key_list({$table})")->getResult(); $indexes = []; foreach ($query as $row) { - $indexes[$row->id]['constraint_name'][] = $row->from; + $indexes[$row->id]['constraint_name'] = null; + $indexes[$row->id]['table_name'] = $table; $indexes[$row->id]['foreign_table_name'] = $row->table; $indexes[$row->id]['column_name'][] = $row->from; $indexes[$row->id]['foreign_column_name'][] = $row->to; @@ -349,26 +347,7 @@ protected function _foreignKeyData(string $table): array $indexes[$row->id]['match'] = $row->match; } - $retVal = []; - - // now we can build index name and convert to object - foreach ($indexes as $row) { - $name = $table . '_' . implode('_', $row['constraint_name']) . '_foreign'; - - $obj = new stdClass(); - $obj->constraint_name = $name; - $obj->table_name = $table; - $obj->column_name = $row['column_name']; - $obj->foreign_table_name = $row['foreign_table_name']; - $obj->foreign_column_name = $row['foreign_column_name']; - $obj->on_delete = $row['on_delete']; - $obj->on_update = $row['on_update']; - $obj->match = $row['match']; - - $retVal[$name] = $obj; - } - - return $retVal; + return $this->foreignKeyDataToObjects($indexes); } /** From 337b00d5c0fc46bf882be953d688b700c9bcaf8f Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 16:49:08 -0700 Subject: [PATCH 15/41] Refactor _processForeignKeys() to remove duplicate code --- system/Database/BaseConnection.php | 18 ++++++++++++++++-- system/Database/Forge.php | 20 +++++++++++--------- system/Database/OCI8/Forge.php | 27 +++------------------------ system/Database/SQLSRV/Forge.php | 25 ++----------------------- 4 files changed, 32 insertions(+), 58 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 2fd8d8e5b56f..c33bc71fb80d 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1543,8 +1543,22 @@ public function getForeignKeyData(string $table) /** * Converts array of arrays generated by _foreignKeyData() to array of objects - */ - protected function foreignKeyDataToObjects(array $data): array + * + * @return array[ + * {constraint_name} => + * stdClass[ + * 'constraint_name' => string, + * 'table_name' => string, + * 'column_name' => string[], + * 'foreign_table_name' => string, + * 'foreign_column_name' => string[], + * 'on_delete' => string, + * 'on_update' => string, + * 'match' => string + * ] + * ] + */ + protected function foreignKeyDataToObjects(array $data) { $retVal = []; diff --git a/system/Database/Forge.php b/system/Database/Forge.php index c2738079b78b..937b75b2dedd 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1055,17 +1055,19 @@ protected function _processIndexes(string $table) return $sqls; } - protected function _processForeignKeys(string $table): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true): string { $sql = ''; - $allowActions = [ - 'CASCADE', - 'SET NULL', - 'NO ACTION', - 'RESTRICT', - 'SET DEFAULT', - ]; + if (empty($allowActions)) { + $allowActions = [ + 'CASCADE', + 'SET NULL', + 'NO ACTION', + 'RESTRICT', + 'SET DEFAULT', + ]; + } foreach ($this->foreignKeys as $fkey) { $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; @@ -1081,7 +1083,7 @@ protected function _processForeignKeys(string $table): string $sql .= ' ON DELETE ' . $fkey['onDelete']; } - if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { + if ($onUpdate === true && $fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { $sql .= ' ON UPDATE ' . $fkey['onUpdate']; } } diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 12bd8b64e0b6..afd9bc3c08e0 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -273,31 +273,10 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) return $sql; } - protected function _processForeignKeys(string $table): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = false): string { - $sql = ''; - - $allowActions = [ - 'CASCADE', - 'SET NULL', - 'NO ACTION', - ]; - - foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; - $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); - $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); - $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); - $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); - - $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; - $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); - - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { - $sql .= ' ON DELETE ' . $fkey['onDelete']; - } - } + $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; - return $sql; + return parent::_processForeignKeys($table, $allowActions, $onUpdate); } } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 7f8a0d8199e2..77ac8455c14e 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -293,32 +293,11 @@ protected function _processColumn(array $field): string * * @param string $table Table name */ - protected function _processForeignKeys(string $table): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true): string { - $sql = ''; - $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; - foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; - $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); - $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); - $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); - $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); - - $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; - $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); - - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { - $sql .= ' ON DELETE ' . $fkey['onDelete']; - } - - if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { - $sql .= ' ON UPDATE ' . $fkey['onUpdate']; - } - } - - return $sql; + return parent::_processForeignKeys($table, $allowActions, $onUpdate); } /** From 2b81be7425b6015973dce3d6b90f155c0bb8e61d Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 16:54:54 -0700 Subject: [PATCH 16/41] Change All DBMS to use fk_ prefix instead of _foreign suffix Use prefix instead of suffix. Oracle 12 only allows 30 byte names. Suffix could be cut off if name too long. --- system/Database/BaseConnection.php | 2 +- system/Database/Forge.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index c33bc71fb80d..1ac06c88f377 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1567,7 +1567,7 @@ protected function foreignKeyDataToObjects(array $data) // for sqlite generate name if ($name === null) { - $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_foreign'; + $name = 'fk_' . $row['table_name'] . '_' . implode('_', $row['column_name']); } $obj = new stdClass(); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 937b75b2dedd..0b4592a42971 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1070,7 +1070,7 @@ protected function _processForeignKeys(string $table, array $allowActions = [], } foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + $nameIndex = 'fk_' . $table . '_' . implode('_', $fkey['field']); $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); From 6813030e662104b3ab38776e3fa0a4cb495d2ee6 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 17:04:16 -0700 Subject: [PATCH 17/41] Refactor _processForeignKeys() to provide 30 byte name for Oracle < version 18 --- system/Database/Forge.php | 9 +++++++-- system/Database/OCI8/Forge.php | 8 ++++++-- system/Database/SQLSRV/Forge.php | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 0b4592a42971..e7212e100ae3 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1055,7 +1055,7 @@ protected function _processIndexes(string $table) return $sqls; } - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string { $sql = ''; @@ -1070,7 +1070,12 @@ protected function _processForeignKeys(string $table, array $allowActions = [], } foreach ($this->foreignKeys as $fkey) { - $nameIndex = 'fk_' . $table . '_' . implode('_', $fkey['field']); + $nameIndex = 'fk_' . $table . '_' . implode('_', $fkey['field']); + + if ($shortName === true && strlen($nameIndex) > 30) { + $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); + } + $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index afd9bc3c08e0..4b65438cbf70 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -273,10 +273,14 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) return $sql; } - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = false): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = false, bool $shortName = false): string { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; - return parent::_processForeignKeys($table, $allowActions, $onUpdate); + if (version_compare($this->db->getVersion(), '18.0.0', '<')) { + $shortName = true; + } + + return parent::_processForeignKeys($table, $allowActions, $onUpdate, $shortName); } } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 77ac8455c14e..b4589e223480 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -293,11 +293,11 @@ protected function _processColumn(array $field): string * * @param string $table Table name */ - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true): string + protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; - return parent::_processForeignKeys($table, $allowActions, $onUpdate); + return parent::_processForeignKeys($table, $allowActions, $onUpdate, $shortName); } /** From 8513959c99755661036c68555e1791fa3e54fc44 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 18:05:33 -0700 Subject: [PATCH 18/41] Revert _foreign suffix Using a prefix causes problems when using dropForeignKey(). --- system/Database/BaseConnection.php | 2 +- system/Database/Forge.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 1ac06c88f377..c33bc71fb80d 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1567,7 +1567,7 @@ protected function foreignKeyDataToObjects(array $data) // for sqlite generate name if ($name === null) { - $name = 'fk_' . $row['table_name'] . '_' . implode('_', $row['column_name']); + $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_foreign'; } $obj = new stdClass(); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index e7212e100ae3..0a7e75d52cdc 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1070,7 +1070,7 @@ protected function _processForeignKeys(string $table, array $allowActions = [], } foreach ($this->foreignKeys as $fkey) { - $nameIndex = 'fk_' . $table . '_' . implode('_', $fkey['field']); + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; if ($shortName === true && strlen($nameIndex) > 30) { $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); From fb070562b13d9813ffea930ce58c5ee4f4801eee Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 18:26:41 -0700 Subject: [PATCH 19/41] Update documentation - upgrade instructions - and change logs --- user_guide_src/source/database/metadata.rst | 4 +-- .../source/database/metadata/009.php | 16 +++++++---- .../source/installation/upgrade_430.rst | 28 +++++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 41aa41ef9784..a54aac4956d3 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -136,6 +136,4 @@ Usage example: .. literalinclude:: metadata/009.php -The object fields may be unique to the database you are using. For instance, SQLite3 does -not return data on column names, but has the additional *sequence* field for compound -foreign key definitions. +Foreign keys use the naming convention ``tableprefix_table_column1_column2_foreign``. diff --git a/user_guide_src/source/database/metadata/009.php b/user_guide_src/source/database/metadata/009.php index c4878c5186b2..26b832b0e2b1 100644 --- a/user_guide_src/source/database/metadata/009.php +++ b/user_guide_src/source/database/metadata/009.php @@ -4,10 +4,14 @@ $keys = $db->getForeignKeyData('table_name'); -foreach ($keys as $key) { - echo $key->constraint_name; - echo $key->table_name; - echo $key->column_name; - echo $key->foreign_table_name; - echo $key->foreign_column_name; +foreach ($keys as $key => $object) { + echo $key === $object->constraint_name; + echo $object->constraint_name; + echo $object->table_name; + echo $object->column_name[0]; // array + echo $object->foreign_table_name; + echo $object->foreign_column_name[0]; // array + echo $object->on_delete; + echo $object->on_update; + echo $object->match; } diff --git a/user_guide_src/source/installation/upgrade_430.rst b/user_guide_src/source/installation/upgrade_430.rst index 5d9aac504cdb..73ea7f641a79 100644 --- a/user_guide_src/source/installation/upgrade_430.rst +++ b/user_guide_src/source/installation/upgrade_430.rst @@ -122,6 +122,34 @@ Interface Changes Some interfaces has been fixed. See :ref:`v430-interface-changes` for details. +Foreign Key Data +===================================================== + +- The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. +You will need to adjust any code depending on this method to use the new structure. +- The Foreign key naming convention has been changed for Oracle. All DBMS use the same +convention now. Oracle version 12 is truncated to 30 bytes. + +Example: ``tableprefix_table_column1_column2_foreign`` + +The data returned has the following structure:: + + /** + * @return array[ + * {constraint_name} => + * stdClass[ + * 'constraint_name' => string, + * 'table_name' => string, + * 'column_name' => string[], + * 'foreign_table_name' => string, + * 'foreign_column_name' => string[], + * 'on_delete' => string, + * 'on_update' => string, + * 'match' => string + * ] + * ] + */ + Others ====== From 8064d3ef45e98fb76bd77994e88922f67c3b40c1 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 2 Sep 2022 18:32:53 -0700 Subject: [PATCH 20/41] Fix user guide syntax error --- user_guide_src/source/installation/upgrade_430.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/installation/upgrade_430.rst b/user_guide_src/source/installation/upgrade_430.rst index 73ea7f641a79..3b51da1bf618 100644 --- a/user_guide_src/source/installation/upgrade_430.rst +++ b/user_guide_src/source/installation/upgrade_430.rst @@ -126,9 +126,9 @@ Foreign Key Data ===================================================== - The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. -You will need to adjust any code depending on this method to use the new structure. + You will need to adjust any code depending on this method to use the new structure. - The Foreign key naming convention has been changed for Oracle. All DBMS use the same -convention now. Oracle version 12 is truncated to 30 bytes. + convention now. Oracle version 12 is truncated to 30 bytes. Example: ``tableprefix_table_column1_column2_foreign`` From 3bc434795d5d18ff18edbd11a20591c472e69857 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sat, 3 Sep 2022 12:17:09 -0700 Subject: [PATCH 21/41] Fix MySql _foreignKeyData() SQL upper case --- system/Database/MySQLi/Connection.php | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/system/Database/MySQLi/Connection.php b/system/Database/MySQLi/Connection.php index be2873ccb98e..0e0b89193062 100644 --- a/system/Database/MySQLi/Connection.php +++ b/system/Database/MySQLi/Connection.php @@ -483,14 +483,14 @@ protected function _foreignKeyData(string $table): array { $sql = ' SELECT - tc.constraint_name, - tc.table_name, - kcu.column_name, - rc.referenced_table_name, - kcu.referenced_column_name, - rc.delete_rule, - rc.update_rule, - rc.match_option + tc.CONSTRAINT_NAME, + tc.TABLE_NAME, + kcu.COLUMN_NAME, + rc.REFERENCED_TABLE_NAME, + kcu.REFERENCED_COLUMN_NAME, + rc.DELETE_RULE, + rc.UPDATE_RULE, + rc.MATCH_OPTION FROM information_schema.table_constraints AS tc INNER JOIN information_schema.referential_constraints AS rc ON tc.constraint_name = rc.constraint_name @@ -511,14 +511,14 @@ protected function _foreignKeyData(string $table): array $indexes = []; foreach ($query as $row) { - $indexes[$row->constraint_name]['constraint_name'] = $row->constraint_name; - $indexes[$row->constraint_name]['table_name'] = $row->table_name; - $indexes[$row->constraint_name]['column_name'][] = $row->column_name; - $indexes[$row->constraint_name]['foreign_table_name'] = $row->referenced_table_name; - $indexes[$row->constraint_name]['foreign_column_name'][] = $row->referenced_column_name; - $indexes[$row->constraint_name]['on_delete'] = $row->delete_rule; - $indexes[$row->constraint_name]['on_update'] = $row->update_rule; - $indexes[$row->constraint_name]['match'] = $row->match_option; + $indexes[$row->CONSTRAINT_NAME]['constraint_name'] = $row->CONSTRAINT_NAME; + $indexes[$row->CONSTRAINT_NAME]['table_name'] = $row->TABLE_NAME; + $indexes[$row->CONSTRAINT_NAME]['column_name'][] = $row->COLUMN_NAME; + $indexes[$row->CONSTRAINT_NAME]['foreign_table_name'] = $row->REFERENCED_TABLE_NAME; + $indexes[$row->CONSTRAINT_NAME]['foreign_column_name'][] = $row->REFERENCED_COLUMN_NAME; + $indexes[$row->CONSTRAINT_NAME]['on_delete'] = $row->DELETE_RULE; + $indexes[$row->CONSTRAINT_NAME]['on_update'] = $row->UPDATE_RULE; + $indexes[$row->CONSTRAINT_NAME]['match'] = $row->MATCH_OPTION; } return $this->foreignKeyDataToObjects($indexes); From cf54786857176089f6b321b3be3862d8a263d409 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sat, 3 Sep 2022 20:41:15 -0700 Subject: [PATCH 22/41] Add DocBlock to _processForeignKeys() --- system/Database/Forge.php | 7 +++++++ system/Database/OCI8/Forge.php | 7 +++++++ system/Database/SQLSRV/Forge.php | 6 ++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 0a7e75d52cdc..e45ab7d578f3 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1055,6 +1055,13 @@ protected function _processIndexes(string $table) return $sqls; } + /** + * Generates SQL to process foreign keys + * + * @param array $allowActions Allows child to override default value + * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) + * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) + */ protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string { $sql = ''; diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 4b65438cbf70..6340a2074dc1 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -273,6 +273,13 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) return $sql; } + /** + * Generates SQL to process foreign keys + * + * @param array $allowActions Allows child to override default value + * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) + * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) + */ protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = false, bool $shortName = false): string { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index b4589e223480..6184b83c8506 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -289,9 +289,11 @@ protected function _processColumn(array $field): string } /** - * Process foreign keys + * Generates SQL to process foreign keys * - * @param string $table Table name + * @param array $allowActions Allows child to override default value + * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) + * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) */ protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string { From 02b6cdf3b413fda4996a745ba0553d3d9b9725b1 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sat, 3 Sep 2022 20:44:56 -0700 Subject: [PATCH 23/41] Change Oracle Version Rule for Identifier length On further review it appears that from 12.2 and on the identifier can be 128 bytes. However a user can set COMPATIBILITY to a lower version which could affect this. --- 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 6340a2074dc1..039d87f44872 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -284,7 +284,7 @@ protected function _processForeignKeys(string $table, array $allowActions = [], { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; - if (version_compare($this->db->getVersion(), '18.0.0', '<')) { + if (version_compare($this->db->getVersion(), '12.2.0', '<')) { $shortName = true; } From a5078aecad4265ab2386aa14bc4d89a4f673c57b Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 6 Sep 2022 11:51:36 -0700 Subject: [PATCH 24/41] Add link to change log --- user_guide_src/source/database/metadata.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index a54aac4956d3..85b7b532d2c6 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -127,6 +127,8 @@ for each key associated with a table. SQLite3 returns a pseudo index named ``PRIMARY``. But it is a special index, and you can't use it in your SQL commands. +.. _metadata-getforeignkeydata: + $db->getForeignKeyData() ------------------------ From 9d3368b21fa53b1f7f274b60ae6f9cdc539de81e Mon Sep 17 00:00:00 2001 From: sclubricants Date: Tue, 6 Sep 2022 15:15:56 -0700 Subject: [PATCH 25/41] revert _processForeignKeys() parameters/structure --- system/Database/Forge.php | 31 ++++++++++--------------------- system/Database/OCI8/Forge.php | 30 ++++++++++++++++++++++-------- system/Database/SQLSRV/Forge.php | 29 +++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index e45ab7d578f3..e4bb97f85be4 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1057,32 +1057,21 @@ protected function _processIndexes(string $table) /** * Generates SQL to process foreign keys - * - * @param array $allowActions Allows child to override default value - * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) - * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) */ - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string + protected function _processForeignKeys(string $table): string { $sql = ''; - if (empty($allowActions)) { - $allowActions = [ - 'CASCADE', - 'SET NULL', - 'NO ACTION', - 'RESTRICT', - 'SET DEFAULT', - ]; - } + $allowActions = [ + 'CASCADE', + 'SET NULL', + 'NO ACTION', + 'RESTRICT', + 'SET DEFAULT', + ]; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; - - if ($shortName === true && strlen($nameIndex) > 30) { - $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); - } - + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); @@ -1095,7 +1084,7 @@ protected function _processForeignKeys(string $table, array $allowActions = [], $sql .= ' ON DELETE ' . $fkey['onDelete']; } - if ($onUpdate === true && $fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { + if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { $sql .= ' ON UPDATE ' . $fkey['onUpdate']; } } diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 039d87f44872..7eb479a8071b 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -275,19 +275,33 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) /** * Generates SQL to process foreign keys - * - * @param array $allowActions Allows child to override default value - * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) - * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) */ - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = false, bool $shortName = false): string + protected function _processForeignKeys(string $table): string { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; - if (version_compare($this->db->getVersion(), '12.2.0', '<')) { - $shortName = true; + $sql = ''; + + foreach ($this->foreignKeys as $fkey) { + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + + if (version_compare($this->db->getVersion(), '12.2.0', '<') && strlen($nameIndex) > 30) { + $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); + } + + $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); + $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); + $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); + $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); + + $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; + $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); + + if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { + $sql .= ' ON DELETE ' . $fkey['onDelete']; + } } - return parent::_processForeignKeys($table, $allowActions, $onUpdate, $shortName); + return $sql; } } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 6184b83c8506..353c92c9b5a3 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -290,16 +290,33 @@ protected function _processColumn(array $field): string /** * Generates SQL to process foreign keys - * - * @param array $allowActions Allows child to override default value - * @param bool $onUpdate Allows child to avoid setting onUpdate rule (Oracle) - * @param bool $shortName Allows child to limit key name to 30 byte (Oracle) */ - protected function _processForeignKeys(string $table, array $allowActions = [], bool $onUpdate = true, bool $shortName = false): string + protected function _processForeignKeys(string $table): string { $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; - return parent::_processForeignKeys($table, $allowActions, $onUpdate, $shortName); + $sql = ''; + + foreach ($this->foreignKeys as $fkey) { + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); + $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); + $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); + $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); + + $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; + $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); + + if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { + $sql .= ' ON DELETE ' . $fkey['onDelete']; + } + + if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { + $sql .= ' ON UPDATE ' . $fkey['onUpdate']; + } + } + + return $sql; } /** From 251e9f98e29ec57ef00cbfd65b532dbed174660f Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 8 Sep 2022 10:20:14 -0700 Subject: [PATCH 26/41] Added breaking change OCI8 name suffix changed from _fk to _foreign --- user_guide_src/source/changelogs/v4.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index 4f344c0c3b2c..6aa922098e56 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -13,7 +13,7 @@ BREAKING ******** - The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. -- The naming of foreign keys in has changed in all DBMS except OCI8. The previous suffix of ``_foreign`` has changed to ``_fk``. +- The naming of foreign keys in OCI8 has changed. The previous suffix of ``_fk`` has changed to ``_foreign``. Behavior Changes ================ From 6738d322348592fdc847c77d8747a1b0495da609 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 12 Sep 2022 17:41:52 -0700 Subject: [PATCH 27/41] Change suffix to _fk for all DBMS It seems its agreed that this is better being shorter. --- system/Database/BaseConnection.php | 2 +- system/Database/Forge.php | 2 +- system/Database/OCI8/Forge.php | 2 +- system/Database/SQLSRV/Forge.php | 2 +- tests/system/Database/Live/ForgeTest.php | 30 +++++++++---------- .../Database/Live/SQLite/AlterTableTest.php | 4 +-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index c33bc71fb80d..677e784d1db2 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1567,7 +1567,7 @@ protected function foreignKeyDataToObjects(array $data) // for sqlite generate name if ($name === null) { - $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_foreign'; + $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_fk'; } $obj = new stdClass(); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index e4bb97f85be4..cd8997feaa57 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1071,7 +1071,7 @@ protected function _processForeignKeys(string $table): string ]; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 7eb479a8071b..adc8bf9df84e 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -283,7 +283,7 @@ protected function _processForeignKeys(string $table): string $sql = ''; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; if (version_compare($this->db->getVersion(), '12.2.0', '<') && strlen($nameIndex) > 30) { $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 353c92c9b5a3..50ad578a724f 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -298,7 +298,7 @@ protected function _processForeignKeys(string $table): string $sql = ''; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_foreign'; + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 304dde462a3f..af25e7d12844 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -485,11 +485,11 @@ public function testForeignKey() $foreignKeyData = $this->db->getForeignKeyData($tableName); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->constraint_name, $this->db->DBPrefix . 'forge_test_invoices_users_id_foreign'); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->column_name, ['users_id']); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->foreign_column_name, ['id']); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->table_name, $this->db->DBPrefix . $tableName); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_foreign']->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->constraint_name, $this->db->DBPrefix . 'forge_test_invoices_users_id_fk'); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->column_name, ['users_id']); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->foreign_column_name, ['id']); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->table_name, $this->db->DBPrefix . $tableName); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); $this->forge->dropTable($tableName, true); $this->forge->dropTable('forge_test_users', true); @@ -522,11 +522,11 @@ public function testForeignKeyAddingWithStringFields() $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_foreign', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->constraint_name); - $this->assertSame(['users_id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->column_name); - $this->assertSame(['id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->foreign_column_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_foreign']->foreign_table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_fk', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->constraint_name); + $this->assertSame(['users_id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->column_name); + $this->assertSame(['id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->foreign_column_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->foreign_table_name); $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); @@ -591,11 +591,11 @@ public function testCompositeForeignKey() $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); $haystack = ['users_id', 'users_second_id']; - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->constraint_name); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->column_name, $haystack); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->constraint_name); + $this->assertSame($foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->column_name, $haystack); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign']->foreign_table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->foreign_table_name); $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); @@ -742,7 +742,7 @@ public function testDropForeignKey() $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE'); $tableName = 'forge_test_invoices'; - $foreignKeyName = 'forge_test_invoices_users_id_foreign'; + $foreignKeyName = 'forge_test_invoices_users_id_fk'; $this->forge->createTable($tableName, true, $attributes); diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 61696ec9d573..1f3675f19cf0 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -224,11 +224,11 @@ public function testDropForeignKeySuccess() $this->createTable('aliens'); $keys = $this->db->getForeignKeyData('aliens'); - $this->assertSame($this->db->DBPrefix . 'aliens_key_id_foreign', $keys[$this->db->DBPrefix . 'aliens_key_id_foreign']->constraint_name); + $this->assertSame($this->db->DBPrefix . 'aliens_key_id_fk', $keys[$this->db->DBPrefix . 'aliens_key_id_fk']->constraint_name); $result = $this->table ->fromTable('aliens') - ->dropForeignKey('aliens_key_id_foreign') + ->dropForeignKey('aliens_key_id_fk') ->run(); $this->assertTrue($result); From 1a6e59df6002469f2cc0f975187dfe139006a5e5 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 12 Sep 2022 17:49:41 -0700 Subject: [PATCH 28/41] Update documentation --- user_guide_src/source/database/metadata.rst | 2 +- user_guide_src/source/installation/upgrade_430.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 85b7b532d2c6..6ff11d4a663e 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -138,4 +138,4 @@ Usage example: .. literalinclude:: metadata/009.php -Foreign keys use the naming convention ``tableprefix_table_column1_column2_foreign``. +Foreign keys use the naming convention ``tableprefix_table_column1_column2_fk``. diff --git a/user_guide_src/source/installation/upgrade_430.rst b/user_guide_src/source/installation/upgrade_430.rst index 3b51da1bf618..f4316f22169c 100644 --- a/user_guide_src/source/installation/upgrade_430.rst +++ b/user_guide_src/source/installation/upgrade_430.rst @@ -127,10 +127,10 @@ Foreign Key Data - The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. You will need to adjust any code depending on this method to use the new structure. -- The Foreign key naming convention has been changed for Oracle. All DBMS use the same - convention now. Oracle version 12 is truncated to 30 bytes. +- The Foreign key naming convention has been changed for all DBMS except Oracle. All DBMS + use the same convention now. Oracle version 12 is truncated to 30 bytes. -Example: ``tableprefix_table_column1_column2_foreign`` +Example: ``tableprefix_table_column1_column2_fk`` The data returned has the following structure:: From 0766b62c964a70e505de929d49e54054f30e5135 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 10:46:47 -0700 Subject: [PATCH 29/41] Add Custom Naming to foreign keys and test Removed special naming for Oracle 12, Added the ability to set foreign key name, and added test. --- system/Database/Forge.php | 10 +++- system/Database/OCI8/Forge.php | 8 +-- system/Database/SQLSRV/Forge.php | 7 ++- system/Database/SQLite3/Forge.php | 9 ++++ tests/system/Database/Live/ForgeTest.php | 67 ++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 7 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index cd8997feaa57..a5a11503fe66 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -401,7 +401,7 @@ public function addField($field) * * @throws DatabaseException */ - public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '') + public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = '') { $fieldName = (array) $fieldName; $tableField = (array) $tableField; @@ -425,6 +425,7 @@ public function addForeignKey($fieldName = '', string $tableName = '', $tableFie 'referenceField' => $tableField, 'onDelete' => strtoupper($onDelete), 'onUpdate' => strtoupper($onUpdate), + 'fkName' => $fkName, ]; return $this; @@ -1071,7 +1072,12 @@ protected function _processForeignKeys(string $table): string ]; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; + if ($fkey['fkName'] !== '') { + $nameIndex = $fkey['fkName']; + } else { + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; + } + $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index adc8bf9df84e..185c435d0be0 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -283,10 +283,10 @@ protected function _processForeignKeys(string $table): string $sql = ''; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; - - if (version_compare($this->db->getVersion(), '12.2.0', '<') && strlen($nameIndex) > 30) { - $nameIndex = substr($nameIndex, 0, 28) . mt_rand(10, 99); + if ($fkey['fkName'] !== '') { + $nameIndex = $fkey['fkName']; + } else { + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; } $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index 50ad578a724f..b082a9fc64d7 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -298,7 +298,12 @@ protected function _processForeignKeys(string $table): string $sql = ''; foreach ($this->foreignKeys as $fkey) { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; + if ($fkey['fkName'] !== '') { + $nameIndex = $fkey['fkName']; + } else { + $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; + } + $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index ef28a9e659e7..eb15a8f7a67e 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -262,4 +262,13 @@ public function dropPrimaryKey(string $table): bool ->dropPrimaryKey() ->run(); } + + public function addForeignKey($fieldName = '', string $tableName = '', $tableField = '', string $onUpdate = '', string $onDelete = '', string $fkName = '') + { + if ($fkName === '') { + return parent::addForeignKey($fieldName, $tableName, $tableField, $onUpdate, $onDelete, $fkName); + } + + throw new DatabaseException('SQLite does not support foreign key names. CodeIgniter will refer to them in the format: prefix_table_column_referencecolumn_fk'); + } } diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index af25e7d12844..a11faef3a745 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -756,6 +756,73 @@ public function testDropForeignKey() $this->forge->dropTable('forge_test_users', true); } + public function testNameForeignKey() + { + if ($this->db->DBDriver === 'SQLite3') { + $this->markTestSkipped('SQLite does not support naming foreign keys.'); + } + + $this->forge->dropTable('forge_test_invoices', true); + $this->forge->dropTable('forge_test_users', true); + + $attributes = []; + + if ($this->db->DBDriver === 'MySQLi') { + $attributes = ['ENGINE' => 'InnoDB']; + } + + $this->forge->addField([ + 'id' => [ + 'type' => 'INTEGER', + 'constraint' => 11, + ], + 'name' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + ], + ]); + $this->forge->addKey('id', true); + $this->forge->createTable('forge_test_users', true, $attributes); + + $this->forge->addField([ + 'id' => [ + 'type' => 'INTEGER', + 'constraint' => 11, + ], + 'users_id' => [ + 'type' => 'INTEGER', + 'constraint' => 11, + ], + 'name' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + ], + ]); + + $foreignKeyName = 'custom_foreign_key_name'; + + $this->forge->addKey('id', true); + $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE', $foreignKeyName); + + $tableName = 'forge_test_invoices'; + + $this->forge->createTable($tableName, true, $attributes); + + $foreignKeyData = $this->db->getForeignKeyData($tableName); + + // see if fk name is found + $this->assertArrayHasKey($foreignKeyName, $foreignKeyData); + + $this->forge->dropForeignKey($tableName, $foreignKeyName); + + $foreignKeyData = $this->db->getForeignKeyData($tableName); + + $this->assertEmpty($foreignKeyData); + + $this->forge->dropTable($tableName, true); + $this->forge->dropTable('forge_test_users', true); + } + public function testAddColumn() { $this->forge->dropTable('forge_test_table', true); From 0c68216ea76bda227826788a04b5d28ce5c7f3e1 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 11:22:57 -0700 Subject: [PATCH 30/41] Removed db prefix from dropForeignKey() Adding the db prefix was causing error when using custom foreign key names. Also refactored _processForeignKeys() to reduce duplicated code. --- system/Database/Forge.php | 21 ++++++------ system/Database/OCI8/Forge.php | 39 ++++----------------- system/Database/SQLSRV/Forge.php | 43 ++++-------------------- tests/system/Database/Live/ForgeTest.php | 2 +- 4 files changed, 25 insertions(+), 80 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index a5a11503fe66..48b893b3f634 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -176,6 +176,13 @@ class Forge */ protected $dropIndexStr = 'DROP INDEX %s ON %s'; + /** + * Foreign Key Allowed Actions + * + * @var array + */ + protected $fkAllowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; + /** * Constructor. */ @@ -481,7 +488,7 @@ public function dropForeignKey(string $table, string $foreignName) $sql = sprintf( (string) $this->dropConstraintStr, $this->db->escapeIdentifiers($this->db->DBPrefix . $table), - $this->db->escapeIdentifiers($this->db->DBPrefix . $foreignName) + $this->db->escapeIdentifiers($foreignName) ); if ($sql === '') { @@ -1063,14 +1070,6 @@ protected function _processForeignKeys(string $table): string { $sql = ''; - $allowActions = [ - 'CASCADE', - 'SET NULL', - 'NO ACTION', - 'RESTRICT', - 'SET DEFAULT', - ]; - foreach ($this->foreignKeys as $fkey) { if ($fkey['fkName'] !== '') { $nameIndex = $fkey['fkName']; @@ -1086,11 +1085,11 @@ protected function _processForeignKeys(string $table): string $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { + if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $this->fkAllowActions, true)) { $sql .= ' ON DELETE ' . $fkey['onDelete']; } - if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { + if ($this->db->DBDriver !== 'OCI8' && $fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $this->fkAllowActions, true)) { $sql .= ' ON UPDATE ' . $fkey['onUpdate']; } } diff --git a/system/Database/OCI8/Forge.php b/system/Database/OCI8/Forge.php index 185c435d0be0..8acbd246c063 100644 --- a/system/Database/OCI8/Forge.php +++ b/system/Database/OCI8/Forge.php @@ -83,6 +83,13 @@ class Forge extends BaseForge */ protected $dropConstraintStr = 'ALTER TABLE %s DROP CONSTRAINT %s'; + /** + * Foreign Key Allowed Actions + * + * @var array + */ + protected $fkAllowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; + /** * ALTER TABLE * @@ -272,36 +279,4 @@ protected function _dropTable(string $table, bool $ifExists, bool $cascade) return $sql; } - - /** - * Generates SQL to process foreign keys - */ - protected function _processForeignKeys(string $table): string - { - $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION']; - - $sql = ''; - - foreach ($this->foreignKeys as $fkey) { - if ($fkey['fkName'] !== '') { - $nameIndex = $fkey['fkName']; - } else { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; - } - - $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); - $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); - $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); - $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); - - $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; - $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); - - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { - $sql .= ' ON DELETE ' . $fkey['onDelete']; - } - } - - return $sql; - } } diff --git a/system/Database/SQLSRV/Forge.php b/system/Database/SQLSRV/Forge.php index b082a9fc64d7..e9625018cb00 100755 --- a/system/Database/SQLSRV/Forge.php +++ b/system/Database/SQLSRV/Forge.php @@ -83,6 +83,13 @@ class Forge extends BaseForge 'REAL' => 'FLOAT', ]; + /** + * Foreign Key Allowed Actions + * + * @var array + */ + protected $fkAllowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; + /** * CREATE TABLE IF statement * @@ -288,42 +295,6 @@ protected function _processColumn(array $field): string . $field['unique']; } - /** - * Generates SQL to process foreign keys - */ - protected function _processForeignKeys(string $table): string - { - $allowActions = ['CASCADE', 'SET NULL', 'NO ACTION', 'RESTRICT', 'SET DEFAULT']; - - $sql = ''; - - foreach ($this->foreignKeys as $fkey) { - if ($fkey['fkName'] !== '') { - $nameIndex = $fkey['fkName']; - } else { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; - } - - $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); - $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); - $referenceTableFilled = $this->db->escapeIdentifiers($this->db->DBPrefix . $fkey['referenceTable']); - $referenceFieldFilled = implode(', ', $this->db->escapeIdentifiers($fkey['referenceField'])); - - $formatSql = ",\n\tCONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s)"; - $sql .= sprintf($formatSql, $nameIndexFilled, $foreignKeyFilled, $referenceTableFilled, $referenceFieldFilled); - - if ($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions, true)) { - $sql .= ' ON DELETE ' . $fkey['onDelete']; - } - - if ($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions, true)) { - $sql .= ' ON UPDATE ' . $fkey['onUpdate']; - } - } - - return $sql; - } - /** * Process primary keys */ diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index a11faef3a745..1ae07e5fad27 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -742,7 +742,7 @@ public function testDropForeignKey() $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE'); $tableName = 'forge_test_invoices'; - $foreignKeyName = 'forge_test_invoices_users_id_fk'; + $foreignKeyName = $this->db->DBPrefix . 'forge_test_invoices_users_id_fk'; $this->forge->createTable($tableName, true, $attributes); From 2db2af47b6ed512097fbba44868de4677b0f6317 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 11:41:24 -0700 Subject: [PATCH 31/41] Update documentation for custom named foreign keys --- user_guide_src/source/changelogs/v4.3.0.rst | 5 ++--- user_guide_src/source/dbmgmt/forge.rst | 5 +++-- user_guide_src/source/dbmgmt/forge/012.php | 4 ++-- user_guide_src/source/dbmgmt/forge/013.php | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index 6aa922098e56..f11e15654009 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -12,9 +12,6 @@ Release Date: Unreleased BREAKING ******** -- The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. -- The naming of foreign keys in OCI8 has changed. The previous suffix of ``_fk`` has changed to ``_foreign``. - Behavior Changes ================ @@ -48,6 +45,7 @@ Others - ``RouteCollection::resetRoutes()`` resets Auto-Discovery of Routes. Previously once discovered, RouteCollection never discover Routes files again even if ``RouteCollection::resetRoutes()`` is called. - ``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 ``BaseConnection::getForeignKeyData()`` has been changed. .. _v430-interface-changes: @@ -121,6 +119,7 @@ Database - The new method ``Forge::dropPrimaryKey()`` allows dropping the primary key on a table. See :ref:`dropping-a-primary-key`. - Improved the SQL structure for ``Builder::updateBatch()``. See :ref:`update-batch` for the details. - Improved data returned by ``BaseConnection::getForeignKeyData()``. All DBMS return the same structure and use the same naming conventions. +- ``Forge::addForeignKey()`` now includes a name parameter to manuall set foreign key names. Model ===== diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 9193df744292..9406d1229eb5 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -179,7 +179,7 @@ you may add them directly in forge: .. literalinclude:: forge/012.php -You can specify the desired action for the "on delete" and "on update" properties of the constraint: +You can specify the desired action for the "on delete" and "on update" properties of the constraint as well as the name: .. literalinclude:: forge/013.php @@ -323,13 +323,14 @@ Class Reference Adds a field to the set that will be used to create a table. Usage: See `Adding Fields`_. - .. php:method:: addForeignKey($fieldName, $tableName, $tableField[, $onUpdate = '', $onDelete = '']) + .. php:method:: addForeignKey($fieldName, $tableName, $tableField[, $onUpdate = '', $onDelete = '', $fkName = '']) :param string|string[] $fieldName: Name of a key field or an array of fields :param string $tableName: Name of a parent table :param string|string[] $tableField: Name of a parent table field or an array of fields :param string $onUpdate: Desired action for the "on update" :param string $onDelete: Desired action for the "on delete" + :param string $fkName: Name of foreign key :returns: \CodeIgniter\Database\Forge instance (method chaining) :rtype: \CodeIgniter\Database\Forge diff --git a/user_guide_src/source/dbmgmt/forge/012.php b/user_guide_src/source/dbmgmt/forge/012.php index dc6ccdb24ec5..5098c4dd1b2b 100644 --- a/user_guide_src/source/dbmgmt/forge/012.php +++ b/user_guide_src/source/dbmgmt/forge/012.php @@ -1,7 +1,7 @@ addForeignKey('users_id', 'users', 'id'); -// gives CONSTRAINT `TABLENAME_users_foreign` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) +// gives CONSTRAINT `TABLENAME_users_id_fk` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) $forge->addForeignKey(['users_id', 'users_name'], 'users', ['id', 'name']); -// gives CONSTRAINT `TABLENAME_users_foreign` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) +// gives CONSTRAINT `TABLENAME_users_id_fk` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) diff --git a/user_guide_src/source/dbmgmt/forge/013.php b/user_guide_src/source/dbmgmt/forge/013.php index 33aa485380ee..fc789da8ba9a 100644 --- a/user_guide_src/source/dbmgmt/forge/013.php +++ b/user_guide_src/source/dbmgmt/forge/013.php @@ -1,7 +1,7 @@ addForeignKey('users_id', 'users', 'id', 'CASCADE', 'CASCADE'); -// gives CONSTRAINT `TABLENAME_users_foreign` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE +$forge->addForeignKey('users_id', 'users', 'id', 'CASCADE', 'CASCADE', 'my_fk_name'); +// gives CONSTRAINT `my_fk_name` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -$forge->addForeignKey(['users_id', 'users_name'], 'users', ['id', 'name'], 'CASCADE', 'CASCADE'); -// gives CONSTRAINT `TABLENAME_users_foreign` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) ON DELETE CASCADE ON UPDATE CASCADE +$forge->addForeignKey(['users_id', 'users_name'], 'users', ['id', 'name'], 'CASCADE', 'CASCADE', 'my_fk_name'); +// gives CONSTRAINT `my_fk_name` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) ON DELETE CASCADE ON UPDATE CASCADE From 5bcb3e3ec967b4c59fcaf02ebec646a291e15054 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 12:02:35 -0700 Subject: [PATCH 32/41] Fix Rector issue --- system/Database/Forge.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/system/Database/Forge.php b/system/Database/Forge.php index 48b893b3f634..a6a275a6aeda 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1071,11 +1071,9 @@ protected function _processForeignKeys(string $table): string $sql = ''; foreach ($this->foreignKeys as $fkey) { - if ($fkey['fkName'] !== '') { - $nameIndex = $fkey['fkName']; - } else { - $nameIndex = $table . '_' . implode('_', $fkey['field']) . '_fk'; - } + $nameIndex = $fkey['fkName'] !== '' ? + $fkey['fkName'] : + $table . '_' . implode('_', $fkey['field']) . '_fk'; $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); From 2c418cdcc1340b9421f0b8609d7bf93bd164744d Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 17:04:12 -0700 Subject: [PATCH 33/41] Update user_guide_src/source/changelogs/v4.3.0.rst Co-authored-by: Michal Sniatala --- user_guide_src/source/changelogs/v4.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index f11e15654009..48a443f00153 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -119,7 +119,7 @@ Database - The new method ``Forge::dropPrimaryKey()`` allows dropping the primary key on a table. See :ref:`dropping-a-primary-key`. - Improved the SQL structure for ``Builder::updateBatch()``. See :ref:`update-batch` for the details. - Improved data returned by ``BaseConnection::getForeignKeyData()``. All DBMS return the same structure and use the same naming conventions. -- ``Forge::addForeignKey()`` now includes a name parameter to manuall set foreign key names. +- ``Forge::addForeignKey()`` now includes a name parameter to manual set foreign key names. Not supported in SQLite3. Model ===== From 3c8a1a5dd78dea79091da3bb1b7cb062f232f340 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Thu, 15 Sep 2022 17:11:33 -0700 Subject: [PATCH 34/41] Added Note about SQLite to Forge Documentation --- user_guide_src/source/dbmgmt/forge.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 9406d1229eb5..15b79adc1107 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -330,7 +330,7 @@ Class Reference :param string|string[] $tableField: Name of a parent table field or an array of fields :param string $onUpdate: Desired action for the "on update" :param string $onDelete: Desired action for the "on delete" - :param string $fkName: Name of foreign key + :param string $fkName: Name of foreign key *Does not work with SQLite :returns: \CodeIgniter\Database\Forge instance (method chaining) :rtype: \CodeIgniter\Database\Forge From 9d9bc4ca7e705998fdc0c05cddafc07e3eb57ee0 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 16 Sep 2022 09:02:14 -0700 Subject: [PATCH 35/41] Update forge.rst --- user_guide_src/source/dbmgmt/forge.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 15b79adc1107..15143d374467 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -330,7 +330,7 @@ Class Reference :param string|string[] $tableField: Name of a parent table field or an array of fields :param string $onUpdate: Desired action for the "on update" :param string $onDelete: Desired action for the "on delete" - :param string $fkName: Name of foreign key *Does not work with SQLite + :param string $fkName: Name of foreign key. This does not work with SQLite :returns: \CodeIgniter\Database\Forge instance (method chaining) :rtype: \CodeIgniter\Database\Forge From d205a5b8ccdd1cb94699790f31ff34c4753d87f5 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sat, 17 Sep 2022 14:57:16 -0700 Subject: [PATCH 36/41] Revert suffix _fk to _foreign in all but OCI8 Kept one test to test default foreign key names - the rest set their own names. Removed the extra test to test setting names since this is done in other tests. --- system/Database/BaseConnection.php | 2 +- system/Database/Forge.php | 2 +- system/Database/SQLite3/Forge.php | 2 +- tests/system/Database/Live/ForgeTest.php | 135 +++++++----------- .../Database/Live/SQLite/AlterTableTest.php | 4 +- 5 files changed, 56 insertions(+), 89 deletions(-) diff --git a/system/Database/BaseConnection.php b/system/Database/BaseConnection.php index 677e784d1db2..c33bc71fb80d 100644 --- a/system/Database/BaseConnection.php +++ b/system/Database/BaseConnection.php @@ -1567,7 +1567,7 @@ protected function foreignKeyDataToObjects(array $data) // for sqlite generate name if ($name === null) { - $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_fk'; + $name = $row['table_name'] . '_' . implode('_', $row['column_name']) . '_foreign'; } $obj = new stdClass(); diff --git a/system/Database/Forge.php b/system/Database/Forge.php index a6a275a6aeda..eabed06ed6ec 100644 --- a/system/Database/Forge.php +++ b/system/Database/Forge.php @@ -1073,7 +1073,7 @@ protected function _processForeignKeys(string $table): string foreach ($this->foreignKeys as $fkey) { $nameIndex = $fkey['fkName'] !== '' ? $fkey['fkName'] : - $table . '_' . implode('_', $fkey['field']) . '_fk'; + $table . '_' . implode('_', $fkey['field']) . ($this->db->DBDriver === 'OCI8' ? '_fk' : '_foreign'); $nameIndexFilled = $this->db->escapeIdentifiers($nameIndex); $foreignKeyFilled = implode(', ', $this->db->escapeIdentifiers($fkey['field'])); diff --git a/system/Database/SQLite3/Forge.php b/system/Database/SQLite3/Forge.php index eb15a8f7a67e..5538777f610b 100644 --- a/system/Database/SQLite3/Forge.php +++ b/system/Database/SQLite3/Forge.php @@ -269,6 +269,6 @@ public function addForeignKey($fieldName = '', string $tableName = '', $tableFie return parent::addForeignKey($fieldName, $tableName, $tableField, $onUpdate, $onDelete, $fkName); } - throw new DatabaseException('SQLite does not support foreign key names. CodeIgniter will refer to them in the format: prefix_table_column_referencecolumn_fk'); + throw new DatabaseException('SQLite does not support foreign key names. CodeIgniter will refer to them in the format: prefix_table_column_referencecolumn_foreign'); } } diff --git a/tests/system/Database/Live/ForgeTest.php b/tests/system/Database/Live/ForgeTest.php index 1ae07e5fad27..978550939b09 100644 --- a/tests/system/Database/Live/ForgeTest.php +++ b/tests/system/Database/Live/ForgeTest.php @@ -485,11 +485,16 @@ public function testForeignKey() $foreignKeyData = $this->db->getForeignKeyData($tableName); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->constraint_name, $this->db->DBPrefix . 'forge_test_invoices_users_id_fk'); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->column_name, ['users_id']); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->foreign_column_name, ['id']); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->table_name, $this->db->DBPrefix . $tableName); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . $tableName . '_users_id_fk']->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); + $foreignKeyName = $this->db->DBPrefix . $tableName . '_users_id_foreign'; + if ($this->db->DBDriver === 'OCI8') { + $foreignKeyName = $this->db->DBPrefix . $tableName . '_users_id_fk'; + } + + $this->assertSame($foreignKeyData[$foreignKeyName]->constraint_name, $foreignKeyName); + $this->assertSame($foreignKeyData[$foreignKeyName]->column_name, ['users_id']); + $this->assertSame($foreignKeyData[$foreignKeyName]->foreign_column_name, ['id']); + $this->assertSame($foreignKeyData[$foreignKeyName]->table_name, $this->db->DBPrefix . $tableName); + $this->assertSame($foreignKeyData[$foreignKeyName]->foreign_table_name, $this->db->DBPrefix . 'forge_test_users'); $this->forge->dropTable($tableName, true); $this->forge->dropTable('forge_test_users', true); @@ -511,22 +516,24 @@ public function testForeignKeyAddingWithStringFields() '`name` VARCHAR(255) NOT NULL', ])->createTable('forge_test_users', true, $attributes); + $foreignKeyName = 'my_custom_fk'; + $this->forge ->addField([ '`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', '`users_id` INT(11) NOT NULL', '`name` VARCHAR(255) NOT NULL', ]) - ->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE') + ->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE', $foreignKeyName) ->createTable('forge_test_invoices', true, $attributes); $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_fk', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->constraint_name); - $this->assertSame(['users_id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->column_name); - $this->assertSame(['id'], $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->foreign_column_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_fk']->foreign_table_name); + $this->assertSame($foreignKeyName, $foreignKeyData[$foreignKeyName]->constraint_name); + $this->assertSame(['users_id'], $foreignKeyData[$foreignKeyName]->column_name); + $this->assertSame(['id'], $foreignKeyData[$foreignKeyName]->foreign_column_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$foreignKeyName]->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$foreignKeyName]->foreign_table_name); $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); @@ -584,18 +591,25 @@ public function testCompositeForeignKey() $this->forge->addField($fields); $this->forge->addPrimaryKey('id'); - $this->forge->addForeignKey(['users_id', 'users_second_id'], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE'); + + $foreignKeyName = 'my_custom_fk'; + + if ($this->db->DBDriver === 'SQLite3') { + $foreignKeyName = $this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_foreign'; + } + + $this->forge->addForeignKey(['users_id', 'users_second_id'], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE', ($this->db->DBDriver !== 'SQLite3' ? $foreignKeyName : '')); $this->forge->createTable('forge_test_invoices', true, $attributes); $foreignKeyData = $this->db->getForeignKeyData('forge_test_invoices'); $haystack = ['users_id', 'users_second_id']; - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->constraint_name); - $this->assertSame($foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->column_name, $haystack); + $this->assertSame($foreignKeyName, $foreignKeyData[$foreignKeyName]->constraint_name); + $this->assertSame($foreignKeyData[$foreignKeyName]->column_name, $haystack); - $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->table_name); - $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$this->db->DBPrefix . 'forge_test_invoices_users_id_users_second_id_fk']->foreign_table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_invoices', $foreignKeyData[$foreignKeyName]->table_name); + $this->assertSame($this->db->DBPrefix . 'forge_test_users', $foreignKeyData[$foreignKeyName]->foreign_table_name); $this->forge->dropTable('forge_test_invoices', true); $this->forge->dropTable('forge_test_users', true); @@ -607,7 +621,11 @@ public function testCompositeForeignKey() public function testCompositeForeignKeyFieldNotExistException() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('Field "user_id, user_second_id" not found.'); + if ($this->db->DBDriver === 'SQLite3') { + $this->expectExceptionMessage('SQLite does not support foreign key names. CodeIgniter will refer to them in the format: prefix_table_column_referencecolumn_foreign'); + } else { + $this->expectExceptionMessage('Field "user_id, user_second_id" not found.'); + } $attributes = []; @@ -651,7 +669,10 @@ public function testCompositeForeignKeyFieldNotExistException() ], ]); $this->forge->addKey('id', true); - $this->forge->addForeignKey(['user_id', 'user_second_id'], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE'); + + $foreignKeyName = 'forge_test_invoices_fk'; + + $this->forge->addForeignKey(['user_id', 'user_second_id'], 'forge_test_users', ['id', 'second_id'], 'CASCADE', 'CASCADE', $foreignKeyName); $this->forge->createTable('forge_test_invoices', true, $attributes); } @@ -659,7 +680,11 @@ public function testCompositeForeignKeyFieldNotExistException() public function testForeignKeyFieldNotExistException() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('Field "user_id" not found.'); + if ($this->db->DBDriver === 'SQLite3') { + $this->expectExceptionMessage('SQLite does not support foreign key names. CodeIgniter will refer to them in the format: prefix_table_column_referencecolumn_foreign'); + } else { + $this->expectExceptionMessage('Field "user_id" not found.'); + } $attributes = []; @@ -695,7 +720,10 @@ public function testForeignKeyFieldNotExistException() ], ]); $this->forge->addKey('id', true); - $this->forge->addForeignKey('user_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE'); + + $foreignKeyName = 'forge_test_invoices_fk'; + + $this->forge->addForeignKey('user_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE', $foreignKeyName); $this->forge->createTable('forge_test_invoices', true, $attributes); } @@ -739,80 +767,19 @@ public function testDropForeignKey() ], ]); $this->forge->addKey('id', true); - $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE'); - - $tableName = 'forge_test_invoices'; - $foreignKeyName = $this->db->DBPrefix . 'forge_test_invoices_users_id_fk'; - - $this->forge->createTable($tableName, true, $attributes); - - $this->forge->dropForeignKey($tableName, $foreignKeyName); - $foreignKeyData = $this->db->getForeignKeyData($tableName); + $foreignKeyName = 'forge_test_invoices_fk'; - $this->assertEmpty($foreignKeyData); - - $this->forge->dropTable($tableName, true); - $this->forge->dropTable('forge_test_users', true); - } - - public function testNameForeignKey() - { if ($this->db->DBDriver === 'SQLite3') { - $this->markTestSkipped('SQLite does not support naming foreign keys.'); + $foreignKeyName = $this->db->DBPrefix . 'forge_test_invoices_users_id_foreign'; } - $this->forge->dropTable('forge_test_invoices', true); - $this->forge->dropTable('forge_test_users', true); - - $attributes = []; - - if ($this->db->DBDriver === 'MySQLi') { - $attributes = ['ENGINE' => 'InnoDB']; - } - - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]); - $this->forge->addKey('id', true); - $this->forge->createTable('forge_test_users', true, $attributes); - - $this->forge->addField([ - 'id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - ], - 'users_id' => [ - 'type' => 'INTEGER', - 'constraint' => 11, - ], - 'name' => [ - 'type' => 'VARCHAR', - 'constraint' => 255, - ], - ]); - - $foreignKeyName = 'custom_foreign_key_name'; - - $this->forge->addKey('id', true); - $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE', $foreignKeyName); + $this->forge->addForeignKey('users_id', 'forge_test_users', 'id', 'CASCADE', 'CASCADE', ($this->db->DBDriver !== 'SQLite3' ? $foreignKeyName : '')); $tableName = 'forge_test_invoices'; $this->forge->createTable($tableName, true, $attributes); - $foreignKeyData = $this->db->getForeignKeyData($tableName); - - // see if fk name is found - $this->assertArrayHasKey($foreignKeyName, $foreignKeyData); - $this->forge->dropForeignKey($tableName, $foreignKeyName); $foreignKeyData = $this->db->getForeignKeyData($tableName); diff --git a/tests/system/Database/Live/SQLite/AlterTableTest.php b/tests/system/Database/Live/SQLite/AlterTableTest.php index 1f3675f19cf0..61696ec9d573 100644 --- a/tests/system/Database/Live/SQLite/AlterTableTest.php +++ b/tests/system/Database/Live/SQLite/AlterTableTest.php @@ -224,11 +224,11 @@ public function testDropForeignKeySuccess() $this->createTable('aliens'); $keys = $this->db->getForeignKeyData('aliens'); - $this->assertSame($this->db->DBPrefix . 'aliens_key_id_fk', $keys[$this->db->DBPrefix . 'aliens_key_id_fk']->constraint_name); + $this->assertSame($this->db->DBPrefix . 'aliens_key_id_foreign', $keys[$this->db->DBPrefix . 'aliens_key_id_foreign']->constraint_name); $result = $this->table ->fromTable('aliens') - ->dropForeignKey('aliens_key_id_fk') + ->dropForeignKey('aliens_key_id_foreign') ->run(); $this->assertTrue($result); From 73d064606b5f5c94ba63793825f01b56ac829142 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Sat, 17 Sep 2022 15:14:11 -0700 Subject: [PATCH 37/41] Revert Documentation --- user_guide_src/source/database/metadata.rst | 2 +- user_guide_src/source/installation/upgrade_430.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 6ff11d4a663e..85b7b532d2c6 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -138,4 +138,4 @@ Usage example: .. literalinclude:: metadata/009.php -Foreign keys use the naming convention ``tableprefix_table_column1_column2_fk``. +Foreign keys use the naming convention ``tableprefix_table_column1_column2_foreign``. diff --git a/user_guide_src/source/installation/upgrade_430.rst b/user_guide_src/source/installation/upgrade_430.rst index f4316f22169c..eb1099a7ad45 100644 --- a/user_guide_src/source/installation/upgrade_430.rst +++ b/user_guide_src/source/installation/upgrade_430.rst @@ -130,7 +130,7 @@ Foreign Key Data - The Foreign key naming convention has been changed for all DBMS except Oracle. All DBMS use the same convention now. Oracle version 12 is truncated to 30 bytes. -Example: ``tableprefix_table_column1_column2_fk`` +Example: ``tableprefix_table_column1_column2_foreign`` The data returned has the following structure:: From ecc25530a87cc909849ac8a54f08904ad866b57d Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 19 Sep 2022 12:26:24 -0700 Subject: [PATCH 38/41] Update user_guide_src/source/changelogs/v4.3.0.rst Co-authored-by: Michal Sniatala --- user_guide_src/source/changelogs/v4.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index 48a443f00153..d7f2f4b8751c 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -118,7 +118,7 @@ Database - ``BaseConnection::escape()`` now excludes the ``RawSql`` data type. This allows passing SQL strings into data. - The new method ``Forge::dropPrimaryKey()`` allows dropping the primary key on a table. See :ref:`dropping-a-primary-key`. - Improved the SQL structure for ``Builder::updateBatch()``. See :ref:`update-batch` for the details. -- Improved data returned by ``BaseConnection::getForeignKeyData()``. All DBMS return the same structure and use the same naming conventions. +- Improved data returned by ``BaseConnection::getForeignKeyData()``. All DBMS returns the same structure. - ``Forge::addForeignKey()`` now includes a name parameter to manual set foreign key names. Not supported in SQLite3. Model From a6276e7de5b35c5a96545fc65a7e9a9746273edb Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 19 Sep 2022 12:34:36 -0700 Subject: [PATCH 39/41] Update documentation --- user_guide_src/source/database/metadata.rst | 2 +- user_guide_src/source/dbmgmt/forge.rst | 2 ++ user_guide_src/source/dbmgmt/forge/012.php | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/user_guide_src/source/database/metadata.rst b/user_guide_src/source/database/metadata.rst index 85b7b532d2c6..101d0a605683 100644 --- a/user_guide_src/source/database/metadata.rst +++ b/user_guide_src/source/database/metadata.rst @@ -138,4 +138,4 @@ Usage example: .. literalinclude:: metadata/009.php -Foreign keys use the naming convention ``tableprefix_table_column1_column2_foreign``. +Foreign keys use the naming convention ``tableprefix_table_column1_column2_foreign``. Oracle uses a slightly different suffix of ``_fk``. diff --git a/user_guide_src/source/dbmgmt/forge.rst b/user_guide_src/source/dbmgmt/forge.rst index 15143d374467..ed37af0dfacb 100644 --- a/user_guide_src/source/dbmgmt/forge.rst +++ b/user_guide_src/source/dbmgmt/forge.rst @@ -183,6 +183,8 @@ You can specify the desired action for the "on delete" and "on update" propertie .. literalinclude:: forge/013.php +.. note:: SQLite does not support the naming of foreign keys. CodeIgniter will refer to them by ``prefix_table_column_foreign``. + Creating a Table ================ diff --git a/user_guide_src/source/dbmgmt/forge/012.php b/user_guide_src/source/dbmgmt/forge/012.php index 5098c4dd1b2b..7d07425c24f2 100644 --- a/user_guide_src/source/dbmgmt/forge/012.php +++ b/user_guide_src/source/dbmgmt/forge/012.php @@ -1,7 +1,7 @@ addForeignKey('users_id', 'users', 'id'); -// gives CONSTRAINT `TABLENAME_users_id_fk` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) +// gives CONSTRAINT `TABLENAME_users_id_foreign` FOREIGN KEY(`users_id`) REFERENCES `users`(`id`) $forge->addForeignKey(['users_id', 'users_name'], 'users', ['id', 'name']); -// gives CONSTRAINT `TABLENAME_users_id_fk` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) +// gives CONSTRAINT `TABLENAME_users_id_foreign` FOREIGN KEY(`users_id`, `users_name`) REFERENCES `users`(`id`, `name`) From 240724b83b35980d727de6ecf639fd7b5349b982 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Mon, 19 Sep 2022 12:36:11 -0700 Subject: [PATCH 40/41] Update upgrade_430.rst --- user_guide_src/source/installation/upgrade_430.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/user_guide_src/source/installation/upgrade_430.rst b/user_guide_src/source/installation/upgrade_430.rst index eb1099a7ad45..ad11016c7280 100644 --- a/user_guide_src/source/installation/upgrade_430.rst +++ b/user_guide_src/source/installation/upgrade_430.rst @@ -127,8 +127,6 @@ Foreign Key Data - The data structure returned by ``BaseConnection::getForeignKeyData()`` has been changed. You will need to adjust any code depending on this method to use the new structure. -- The Foreign key naming convention has been changed for all DBMS except Oracle. All DBMS - use the same convention now. Oracle version 12 is truncated to 30 bytes. Example: ``tableprefix_table_column1_column2_foreign`` From aa818d5edcdbdb5869922d1b619799085d25d5b5 Mon Sep 17 00:00:00 2001 From: sclubricants Date: Fri, 23 Sep 2022 09:30:21 -0700 Subject: [PATCH 41/41] Add to changelog --- user_guide_src/source/changelogs/v4.3.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/user_guide_src/source/changelogs/v4.3.0.rst b/user_guide_src/source/changelogs/v4.3.0.rst index d7f2f4b8751c..fa0dc83e3ea9 100644 --- a/user_guide_src/source/changelogs/v4.3.0.rst +++ b/user_guide_src/source/changelogs/v4.3.0.rst @@ -89,6 +89,7 @@ Others - The return type of ``CodeIgniter\Database\Database::loadForge()`` has been changed to ``Forge``. - The return type of ``CodeIgniter\Database\Database::loadUtils()`` has been changed to ``BaseUtils``. +- Parameter ``$column`` has changed in ``Table::dropForeignKey()`` to ``$foreignName``. Enhancements ************