SQLite: Allow editing foreign keys

This commit is contained in:
Jakub Vrana 2013-08-09 15:16:15 -07:00
parent 62e582456e
commit 8a1b8910c1
6 changed files with 61 additions and 44 deletions

View file

@ -58,7 +58,12 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
} }
} }
if ($foreign_key !== null) { 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"]); $after = " AFTER " . idf_escape($field["field"]);
} elseif ($field["orig"] != "") { } elseif ($field["orig"] != "") {

View file

@ -482,15 +482,21 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
return true; return true;
} }
function recreate_table($table, $name, $alter, $originals, $foreign, $indexes = array()) { function recreate_table($table, $name, $fields, $originals, $foreign, $indexes = array()) {
queries("BEGIN"); queries("BEGIN");
if ($table != "") { if ($table != "") {
if (!$fields) {
foreach (fields($table) as $key => $field) {
$fields[] = process_field($field, $field);
$originals[$key] = idf_escape($key);
}
}
$primary_key = false; $primary_key = false;
foreach ($alter as $key => $field) { foreach ($fields as $key => $field) {
if ($field[6]) { if ($field[6]) {
$primary_key = true; $primary_key = true;
} }
$alter[$key] = " " . implode($field); $fields[$key] = " " . implode($field);
} }
$drop_indexes = array(); $drop_indexes = array();
foreach ($indexes as $key => $val) { foreach ($indexes as $key => $val) {
@ -520,23 +526,20 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
$foreign[] = " PRIMARY KEY $val[2]"; $foreign[] = " PRIMARY KEY $val[2]";
} }
} }
foreach (foreign_keys($table) as $foreign_key) { foreach (foreign_keys($table) as $key_name => $foreign_key) {
$columns = array(); foreach ($foreign_key["source"] as $key => $column) {
foreach ($foreign_key["source"] as $column) {
if (!$originals[$column]) { if (!$originals[$column]) {
continue 2; 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); $fields = array_merge($fields, array_filter($foreign));
if (!queries("CREATE TABLE " . table($table != "" ? "adminer_$name" : $name) . " (\n" . implode(",\n", $alter) . "\n)")) { if (!queries("CREATE TABLE " . table($table != "" ? "adminer_$name" : $name) . " (\n" . implode(",\n", $fields) . "\n)")) {
// implicit ROLLBACK to not overwrite $connection->error // implicit ROLLBACK to not overwrite $connection->error
return false; return false;
} }
@ -577,13 +580,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
function alter_indexes($table, $alter) { function alter_indexes($table, $alter) {
foreach ($alter as $primary) { foreach ($alter as $primary) {
if ($primary[0] == "PRIMARY") { if ($primary[0] == "PRIMARY") {
$fields = array(); return recreate_table($table, $table, array(), array(), array(), $alter);
$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);
} }
} }
foreach (array_reverse($alter) as $val) { foreach (array_reverse($alter) as $val) {

View file

@ -4,25 +4,28 @@ $name = $_GET["name"];
$row = $_POST; $row = $_POST;
if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-js"]) { if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-js"]) {
$alter = "ALTER TABLE " . table($TABLE); $message = ($_POST["drop"] ? lang('Foreign key has been dropped.') : ($name != "" ? lang('Foreign key has been altered.') : lang('Foreign key has been created.')));
$drop = "\nDROP " . ($jush == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name); $location = ME . "table=" . urlencode($TABLE);
if ($_POST["drop"]) {
query_redirect($alter . $drop, ME . "table=" . urlencode($TABLE), lang('Foreign key has been dropped.')); $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 { } else {
$source = array_filter($row["source"], 'strlen'); $alter = "ALTER TABLE " . table($TABLE);
ksort($source); // enforce input order $drop = "\nDROP " . ($jush == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($name);
$target = array(); if ($_POST["drop"]) {
foreach ($source as $key => $val) { query_redirect($alter . $drop, $location, $message);
$target[$key] = $row["target"][$key]; } 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.') . "<br>$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.') . "<br>$error"; //! no partitioning
} }
} }

View file

@ -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 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 /** Add a file to TAR
* @param string * @param string
* @param TmpFile * @param TmpFile

View file

@ -53,7 +53,7 @@ if ($fields) {
$foreign_keys = foreign_keys($TABLE); $foreign_keys = foreign_keys($TABLE);
if ($foreign_keys) { if ($foreign_keys) {
echo "<table cellspacing='0'>\n"; echo "<table cellspacing='0'>\n";
echo "<thead><tr><th>" . lang('Source') . "<td>" . lang('Target') . "<td>" . lang('ON DELETE') . "<td>" . lang('ON UPDATE') . ($jush != "sqlite" ? "<td>&nbsp;" : "") . "</thead>\n"; echo "<thead><tr><th>" . lang('Source') . "<td>" . lang('Target') . "<td>" . lang('ON DELETE') . "<td>" . lang('ON UPDATE') . "<td>&nbsp;</thead>\n";
foreach ($foreign_keys as $name => $foreign_key) { foreach ($foreign_keys as $name => $foreign_key) {
echo "<tr title='" . h($name) . "'>"; echo "<tr title='" . h($name) . "'>";
echo "<th><i>" . implode("</i>, <i>", array_map('h', $foreign_key["source"])) . "</i>"; echo "<th><i>" . implode("</i>, <i>", array_map('h', $foreign_key["source"])) . "</i>";
@ -64,13 +64,11 @@ if ($fields) {
echo "(<i>" . implode("</i>, <i>", array_map('h', $foreign_key["target"])) . "</i>)"; echo "(<i>" . implode("</i>, <i>", array_map('h', $foreign_key["target"])) . "</i>)";
echo "<td>" . nbsp($foreign_key["on_delete"]) . "\n"; echo "<td>" . nbsp($foreign_key["on_delete"]) . "\n";
echo "<td>" . nbsp($foreign_key["on_update"]) . "\n"; echo "<td>" . nbsp($foreign_key["on_update"]) . "\n";
echo ($jush == "sqlite" ? "" : '<td><a href="' . h(ME . 'foreign=' . urlencode($TABLE) . '&name=' . urlencode($name)) . '">' . lang('Alter') . '</a>'); echo '<td><a href="' . h(ME . 'foreign=' . urlencode($TABLE) . '&name=' . urlencode($name)) . '">' . lang('Alter') . '</a>';
} }
echo "</table>\n"; echo "</table>\n";
} }
if ($jush != "sqlite") { echo '<p class="links"><a href="' . h(ME) . 'foreign=' . urlencode($TABLE) . '">' . lang('Add foreign key') . "</a>\n";
echo '<p class="links"><a href="' . h(ME) . 'foreign=' . urlencode($TABLE) . '">' . lang('Add foreign key') . "</a>\n";
}
} }
if (support("trigger")) { if (support("trigger")) {

View file

@ -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 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 Use MD5 for editing long keys only in supported drivers, bug since Adminer 3.6.4
SQLite: Allow editing primary key SQLite: Allow editing primary key
SQLite: Allow editing foreign keys
PostgreSQL: Fix handling of nextval() default values PostgreSQL: Fix handling of nextval() default values
PostgreSQL: Support creating array columns PostgreSQL: Support creating array columns