From 8a1b8910c133b5a3d9f342fb233b4f2b94d193b1 Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Fri, 9 Aug 2013 15:16:15 -0700 Subject: [PATCH] SQLite: Allow editing foreign keys --- adminer/create.inc.php | 7 +++++- adminer/drivers/sqlite.inc.php | 39 +++++++++++++++------------------ adminer/foreign.inc.php | 37 +++++++++++++++++-------------- adminer/include/editing.inc.php | 13 +++++++++++ adminer/table.inc.php | 8 +++---- changes.txt | 1 + 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/adminer/create.inc.php b/adminer/create.inc.php index fcbfabce..c0e636a9 100644 --- a/adminer/create.inc.php +++ b/adminer/create.inc.php @@ -58,7 +58,12 @@ if ($_POST && !process_fields($row["fields"]) && !$error) { } } if ($foreign_key !== null) { - $foreign[idf_escape($field["field"])] = ($TABLE != "" && $jush != "sqlite" ? "ADD" : " ") . " FOREIGN KEY (" . idf_escape($field["field"]) . ") REFERENCES " . table($foreign_keys[$field["type"]]) . " (" . idf_escape($type_field["field"]) . ")" . (preg_match("~^($on_actions)\$~", $field["on_delete"]) ? " ON DELETE $field[on_delete]" : ""); + $foreign[idf_escape($field["field"])] = ($TABLE != "" && $jush != "sqlite" ? "ADD" : " ") . format_foreign_key(array( + 'table' => $foreign_keys[$field["type"]], + 'source' => array($field["field"]), + 'target' => array($type_field["field"]), + 'on_delete' => $field["on_delete"], + )); } $after = " AFTER " . idf_escape($field["field"]); } elseif ($field["orig"] != "") { diff --git a/adminer/drivers/sqlite.inc.php b/adminer/drivers/sqlite.inc.php index dbfc14c7..8d7c48cd 100644 --- a/adminer/drivers/sqlite.inc.php +++ b/adminer/drivers/sqlite.inc.php @@ -482,15 +482,21 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { return true; } - function recreate_table($table, $name, $alter, $originals, $foreign, $indexes = array()) { + function recreate_table($table, $name, $fields, $originals, $foreign, $indexes = array()) { queries("BEGIN"); if ($table != "") { + if (!$fields) { + foreach (fields($table) as $key => $field) { + $fields[] = process_field($field, $field); + $originals[$key] = idf_escape($key); + } + } $primary_key = false; - foreach ($alter as $key => $field) { + foreach ($fields as $key => $field) { if ($field[6]) { $primary_key = true; } - $alter[$key] = " " . implode($field); + $fields[$key] = " " . implode($field); } $drop_indexes = array(); foreach ($indexes as $key => $val) { @@ -520,23 +526,20 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { $foreign[] = " PRIMARY KEY $val[2]"; } } - foreach (foreign_keys($table) as $foreign_key) { - $columns = array(); - foreach ($foreign_key["source"] as $column) { + foreach (foreign_keys($table) as $key_name => $foreign_key) { + foreach ($foreign_key["source"] as $key => $column) { if (!$originals[$column]) { continue 2; } - $columns[] = $originals[$column]; + $foreign_key["source"][$key] = idf_unescape($originals[$column]); + } + if (!isset($foreign[" $key_name"])) { + $foreign[] = " " . format_foreign_key($foreign_key); } - $foreign[] = " FOREIGN KEY (" . implode(", ", $columns) . ") REFERENCES " - . table($foreign_key["table"]) - . " (" . implode(", ", array_map('idf_escape', $foreign_key["target"])) - . ") ON DELETE $foreign_key[on_delete] ON UPDATE $foreign_key[on_update]" - ; } } - $alter = array_merge($alter, $foreign); - if (!queries("CREATE TABLE " . table($table != "" ? "adminer_$name" : $name) . " (\n" . implode(",\n", $alter) . "\n)")) { + $fields = array_merge($fields, array_filter($foreign)); + if (!queries("CREATE TABLE " . table($table != "" ? "adminer_$name" : $name) . " (\n" . implode(",\n", $fields) . "\n)")) { // implicit ROLLBACK to not overwrite $connection->error return false; } @@ -577,13 +580,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { function alter_indexes($table, $alter) { foreach ($alter as $primary) { if ($primary[0] == "PRIMARY") { - $fields = array(); - $originals = array(); - foreach (fields($table) as $name => $field) { - $fields[] = process_field($field, $field); - $originals[$name] = idf_escape($name); - } - return recreate_table($table, $table, $fields, $originals, array(), $alter); + return recreate_table($table, $table, array(), array(), array(), $alter); } } foreach (array_reverse($alter) as $val) { diff --git a/adminer/foreign.inc.php b/adminer/foreign.inc.php index 9f7e05f2..bad28c2b 100644 --- a/adminer/foreign.inc.php +++ b/adminer/foreign.inc.php @@ -4,25 +4,28 @@ $name = $_GET["name"]; $row = $_POST; if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-js"]) { - $alter = "ALTER TABLE " . table($TABLE); - $drop = "\nDROP " . ($jush == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name); - if ($_POST["drop"]) { - query_redirect($alter . $drop, ME . "table=" . urlencode($TABLE), lang('Foreign key has been dropped.')); + $message = ($_POST["drop"] ? lang('Foreign key has been dropped.') : ($name != "" ? lang('Foreign key has been altered.') : lang('Foreign key has been created.'))); + $location = ME . "table=" . urlencode($TABLE); + + $row["source"] = array_filter($row["source"], 'strlen'); + ksort($row["source"]); // enforce input order + $target = array(); + foreach ($row["source"] as $key => $val) { + $target[$key] = $row["target"][$key]; + } + $row["target"] = $target; + + if ($jush == "sqlite") { + queries_redirect($location, $message, recreate_table($TABLE, $TABLE, array(), array(), array(" $name" => ($_POST["drop"] ? "" : " " . format_foreign_key($row))))); } else { - $source = array_filter($row["source"], 'strlen'); - ksort($source); // enforce input order - $target = array(); - foreach ($source as $key => $val) { - $target[$key] = $row["target"][$key]; + $alter = "ALTER TABLE " . table($TABLE); + $drop = "\nDROP " . ($jush == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name); + if ($_POST["drop"]) { + query_redirect($alter . $drop, $location, $message); + } else { + query_redirect($alter . ($name != "" ? "$drop," : "") . "\nADD" . format_foreign_key($row), $location, $message); + $error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.') . "
$error"; //! no partitioning } - - query_redirect($alter - . ($name != "" ? "$drop," : "") - . "\nADD FOREIGN KEY (" . implode(", ", array_map('idf_escape', $source)) . ") REFERENCES " . table($row["table"]) . " (" . implode(", ", array_map('idf_escape', $target)) . ")" //! reuse $name - check in older MySQL versions - . (preg_match("~^($on_actions)\$~", $row["on_delete"]) ? " ON DELETE $row[on_delete]" : "") - . (preg_match("~^($on_actions)\$~", $row["on_update"]) ? " ON UPDATE $row[on_update]" : "") - , ME . "table=" . urlencode($TABLE), ($name != "" ? lang('Foreign key has been altered.') : lang('Foreign key has been created.'))); - $error = lang('Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.') . "
$error"; //! no partitioning } } diff --git a/adminer/include/editing.inc.php b/adminer/include/editing.inc.php index 42389be4..4f4f615f 100644 --- a/adminer/include/editing.inc.php +++ b/adminer/include/editing.inc.php @@ -436,6 +436,19 @@ function remove_definer($query) { return preg_replace('~^([A-Z =]+) DEFINER=`' . preg_replace('~@(.*)~', '`@`(%|\\1)', logged_user()) . '`~', '\\1', $query); //! proper escaping of user } +/** Format foreign key to use in SQL query +* @param array ("table" => string, "source" => array, "target" => array, "on_delete" => one of $on_actions, "on_update" => one of $on_actions) +* @return +*/ +function format_foreign_key($foreign_key) { + global $on_actions; + return " FOREIGN KEY (" . implode(", ", array_map('idf_escape', $foreign_key["source"])) . ") REFERENCES " . table($foreign_key["table"]) + . " (" . implode(", ", array_map('idf_escape', $foreign_key["target"])) . ")" //! reuse $name - check in older MySQL versions + . (preg_match("~^($on_actions)\$~", $foreign_key["on_delete"]) ? " ON DELETE $foreign_key[on_delete]" : "") + . (preg_match("~^($on_actions)\$~", $foreign_key["on_update"]) ? " ON UPDATE $foreign_key[on_update]" : "") + ; +} + /** Add a file to TAR * @param string * @param TmpFile diff --git a/adminer/table.inc.php b/adminer/table.inc.php index 84a20a82..a84a3718 100644 --- a/adminer/table.inc.php +++ b/adminer/table.inc.php @@ -53,7 +53,7 @@ if ($fields) { $foreign_keys = foreign_keys($TABLE); if ($foreign_keys) { echo "\n"; - echo "\n"; + echo "\n"; foreach ($foreign_keys as $name => $foreign_key) { echo ""; echo "
" . lang('Source') . "" . lang('Target') . "" . lang('ON DELETE') . "" . lang('ON UPDATE') . ($jush != "sqlite" ? " " : "") . "
" . lang('Source') . "" . lang('Target') . "" . lang('ON DELETE') . "" . lang('ON UPDATE') . " 
" . implode(", ", array_map('h', $foreign_key["source"])) . ""; @@ -64,13 +64,11 @@ if ($fields) { echo "(" . implode(", ", array_map('h', $foreign_key["target"])) . ")"; echo "" . nbsp($foreign_key["on_delete"]) . "\n"; echo "" . nbsp($foreign_key["on_update"]) . "\n"; - echo ($jush == "sqlite" ? "" : '' . lang('Alter') . ''); + echo '' . lang('Alter') . ''; } echo "
\n"; } - if ($jush != "sqlite") { - echo '