Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQLite3 now supports dropping foreign keys. Fixes #1982 #2042

Merged
merged 2 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions system/Database/BaseConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1697,6 +1697,30 @@ public function getForeignKeyData(string $table)

//--------------------------------------------------------------------

/**
* Disables foreign key checks temporarily.
*/
public function disableForeignKeyChecks()
{
$sql = $this->_disableForeignKeyChecks();

return $this->query($sql);
}

//--------------------------------------------------------------------

/**
* Enables foreign key checks temporarily.
*/
public function enableForeignKeyChecks()
{
$sql = $this->_enableForeignKeyChecks();

return $this->query($sql);
}

//--------------------------------------------------------------------

/**
* Allows the engine to be set into a mode where queries are not
* actually executed, but they are still generated, timed, etc.
Expand Down
35 changes: 21 additions & 14 deletions system/Database/Forge.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,16 +416,16 @@ public function addForeignKey(string $fieldName = '', string $tableName = '', st
/**
* Foreign Key Drop
*
* @param string $table Table name
* @param string $foreign_name Foreign name
* @param string $table Table name
* @param string $foreignName Foreign name
*
* @return boolean|\CodeIgniter\Database\BaseResult|\CodeIgniter\Database\Query|false|mixed
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
*/
public function dropForeignKey(string $table, string $foreign_name)
public function dropForeignKey(string $table, string $foreignName)
{
$sql = sprintf($this->dropConstraintStr, $this->db->escapeIdentifiers($this->db->DBPrefix . $table),
$this->db->escapeIdentifiers($this->db->DBPrefix . $foreign_name));
$this->db->escapeIdentifiers($this->db->DBPrefix . $foreignName));

if ($sql === false)
{
Expand Down Expand Up @@ -484,7 +484,10 @@ public function createTable(string $table, bool $if_not_exists = false, array $a

if (($result = $this->db->query($sql)) !== false)
{
empty($this->db->dataCache['table_names']) || ($this->db->dataCache['table_names'][] = $table);
if (! isset($this->db->dataCache['table_names'][$table]))
{
$this->db->dataCache['table_names'][] = $table;
}

// Most databases don't support creating indexes from within the CREATE TABLE statement
if (! empty($this->keys))
Expand Down Expand Up @@ -582,16 +585,16 @@ protected function _createTableAttributes(array $attributes): string
/**
* Drop Table
*
* @param string $table_name Table name
* @param boolean $if_exists Whether to add an IF EXISTS condition
* @param boolean $cascade Whether to add an CASCADE condition
* @param string $tableName Table name
* @param boolean $ifExists Whether to add an IF EXISTS condition
* @param boolean $cascade Whether to add an CASCADE condition
*
* @return mixed
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
*/
public function dropTable(string $table_name, bool $if_exists = false, bool $cascade = false)
public function dropTable(string $tableName, bool $ifExists = false, bool $cascade = false)
{
if ($table_name === '')
if ($tableName === '')
{
if ($this->db->DBDebug)
{
Expand All @@ -602,22 +605,26 @@ public function dropTable(string $table_name, bool $if_exists = false, bool $cas
}

// If the prefix is already starting the table name, remove it...
if (! empty($this->db->DBPrefix) && strpos($table_name, $this->db->DBPrefix) === 0)
if (! empty($this->db->DBPrefix) && strpos($tableName, $this->db->DBPrefix) === 0)
{
$table_name = substr($table_name, strlen($this->db->DBPrefix));
$tableName = substr($tableName, strlen($this->db->DBPrefix));
}

if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists, $cascade)) === true)
if (($query = $this->_dropTable($this->db->DBPrefix . $tableName, $ifExists, $cascade)) === true)
{
return true;
}

$this->db->disableForeignKeyChecks();

$query = $this->db->query($query);

$this->db->enableForeignKeyChecks();

// Update table list cache
if ($query && ! empty($this->db->dataCache['table_names']))
{
$key = array_search(strtolower($this->db->DBPrefix . $table_name),
$key = array_search(strtolower($this->db->DBPrefix . $tableName),
array_map('strtolower', $this->db->dataCache['table_names']), true);
if ($key !== false)
{
Expand Down
24 changes: 24 additions & 0 deletions system/Database/MySQLi/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,30 @@ public function _foreignKeyData(string $table): array

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to disable foreign key checks.
*
* @return string
*/
protected function _disableForeignKeyChecks()
{
return 'SET FOREIGN_KEY_CHECKS=0';
}

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to enable foreign key checks.
*
* @return string
*/
protected function _enableForeignKeyChecks()
{
return 'SET FOREIGN_KEY_CHECKS=1';
}

//--------------------------------------------------------------------

/**
* Returns the last error code and message.
*
Expand Down
24 changes: 24 additions & 0 deletions system/Database/Postgre/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,30 @@ public function _foreignKeyData(string $table): array

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to disable foreign key checks.
*
* @return string
*/
protected function _disableForeignKeyChecks()
{
return 'SET CONSTRAINTS ALL DEFERRED';
}

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to enable foreign key checks.
*
* @return string
*/
protected function _enableForeignKeyChecks()
{
return 'SET CONSTRAINTS ALL IMMEDIATE;';
}

//--------------------------------------------------------------------

/**
* Returns the last error code and message.
*
Expand Down
26 changes: 25 additions & 1 deletion system/Database/SQLite3/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,30 @@ public function _foreignKeyData(string $table): array

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to disable foreign key checks.
*
* @return string
*/
protected function _disableForeignKeyChecks()
{
return 'PRAGMA foreign_keys = OFF';
}

//--------------------------------------------------------------------

/**
* Returns platform-specific SQL to enable foreign key checks.
*
* @return string
*/
protected function _enableForeignKeyChecks()
{
return 'PRAGMA foreign_keys = ON';
}

//--------------------------------------------------------------------

/**
* Returns the last error code and message.
*
Expand Down Expand Up @@ -504,7 +528,7 @@ public function isWriteType($sql): bool
*
* @return boolean
*/
protected function supportsForeignKeys(): bool
public function supportsForeignKeys(): bool
{
$result = $this->simpleQuery('PRAGMA foreign_keys');

Expand Down
20 changes: 16 additions & 4 deletions system/Database/SQLite3/Forge.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,27 @@ protected function _attributeAutoIncrement(array &$attributes, array &$field)
/**
* Foreign Key Drop
*
* @param string $table Table name
* @param string $foreign_name Foreign name
* @param string $table Table name
* @param string $foreignName Foreign name
*
* @return boolean
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
*/
public function dropForeignKey(string $table, string $foreign_name): bool
public function dropForeignKey(string $table, string $foreignName): bool
{
throw new DatabaseException(lang('Database.dropForeignKeyUnsupported'));
// If this version of SQLite doesn't support it, we're done here
if ($this->db->supportsForeignKeys() !== true)
{
return true;
}

// Otherwise we have to copy the table and recreate
// without the foreign key being involved now
$sqlTable = new Table($this->db, $this);

return $sqlTable->fromTable($this->db->DBPrefix . $table)
->dropForeignKey($foreignName)
->run();
}

//--------------------------------------------------------------------
Expand Down
34 changes: 34 additions & 0 deletions system/Database/SQLite3/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,40 @@ public function modifyColumn(array $field)
return $this;
}

/**
* Drops a foreign key from this table so that
* it won't be recreated in the future.
*
* @param string $column
*
* @return \CodeIgniter\Database\SQLite3\Table
*/
public function dropForeignKey(string $column)
{
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]);
}

return $this;
}

/**
* Creates the new table based on our current fields.
*
Expand Down
4 changes: 0 additions & 4 deletions tests/system/Database/Live/ForgeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,6 @@ public function testDropForeignKey()
{
$attributes = ['ENGINE' => 'InnoDB'];
}
if ($this->db->DBDriver === 'SQLite3')
{
$this->expectException(DatabaseException::class);
}

$this->forge->addField([
'id' => [
Expand Down
Loading