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 "
" . 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 " |
' . lang('Add foreign key') . "\n"; - } + echo '
' . lang('Add foreign key') . "\n"; } if (support("trigger")) { diff --git a/changes.txt b/changes.txt index c5e0981f..f9f691b9 100644 --- a/changes.txt +++ b/changes.txt @@ -15,6 +15,7 @@ Don't append newlines to uploaded files, bug since Adminer 3.7.0 Don't display SQL edit form on Ctrl+click on the select query, introduced in Adminer 3.6.4 Use MD5 for editing long keys only in supported drivers, bug since Adminer 3.6.4 SQLite: Allow editing primary key +SQLite: Allow editing foreign keys PostgreSQL: Fix handling of nextval() default values PostgreSQL: Support creating array columns