From: Jakub Vrana Date: Wed, 26 Feb 2025 10:59:50 +0000 (+0100) Subject: SQLite: Support CHECK constraint X-Git-Tag: v5.0.0~135 X-Git-Url: https://git.joonet.de/?a=commitdiff_plain;h=e3a2b1b57abecec0d40e8fd60b2a2e6e6c644617;p=adminer.git SQLite: Support CHECK constraint --- diff --git a/adminer/check.inc.php b/adminer/check.inc.php index c6724dbc..e1f6961e 100644 --- a/adminer/check.inc.php +++ b/adminer/check.inc.php @@ -4,9 +4,13 @@ $name = $_GET["name"]; $row = $_POST; if ($row && !$error) { - $result = ($name == "" || queries("ALTER TABLE " . table($TABLE) . " DROP CONSTRAINT " . idf_escape($name))); - if (!$row["drop"]) { - $result = queries("ALTER TABLE " . table($TABLE) . " ADD" . ($row["name"] != "" ? " CONSTRAINT " . idf_escape($row["name"]) . "" : "") . " CHECK ($row[clause])"); //! SQL injection + if ($jush == "sqlite") { + $result = recreate_table($TABLE, $TABLE, array(), array(), array(), 0, array(), $name, ($row["drop"] ? "" : $row["clause"])); + } else { + $result = ($name == "" || queries("ALTER TABLE " . table($TABLE) . " DROP CONSTRAINT " . idf_escape($name))); + if (!$row["drop"]) { + $result = queries("ALTER TABLE " . table($TABLE) . " ADD" . ($row["name"] != "" ? " CONSTRAINT " . idf_escape($row["name"]) . "" : "") . " CHECK ($row[clause])"); //! SQL injection + } } queries_redirect( ME . "table=" . urlencode($TABLE), @@ -18,18 +22,24 @@ if ($row && !$error) { page_header(($name != "" ? lang('Alter check') . ": " . h($name) : lang('Create check')), $error, array("table" => $TABLE)); if (!$row) { - $checks = check_constraints($TABLE); + $checks = $driver->checkConstraints($TABLE); $row = array("name" => $name, "clause" => $checks[$name]); } ?>
-

: " data-maxlength="64" autocapitalize="off"> '; +} +echo doc_link(array( 'sql' => "create-table-check-constraints.html", 'mariadb' => "constraint/", 'pgsql' => "ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS", 'mssql' => "relational-databases/tables/create-check-constraints", -)); ?> + 'sqlite' => "lang_createtable.html#check_constraints", +), "?"); +?>

diff --git a/adminer/drivers/sqlite.inc.php b/adminer/drivers/sqlite.inc.php index 5408806a..bd2921f7 100644 --- a/adminer/drivers/sqlite.inc.php +++ b/adminer/drivers/sqlite.inc.php @@ -227,6 +227,11 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { } } + function checkConstraints($table) { + preg_match_all('~ CHECK *(\( *(((?>[^()]*[^() ])|(?1))*) *\))~', $this->_conn->result("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . q($table)), $matches); //! could be inside a comment + return array_combine($matches[2], $matches[2]); + } + } @@ -523,8 +528,20 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { return true; } - function recreate_table($table, $name, $fields, $originals, $foreign, $auto_increment = 0, $indexes = array()) { - global $connection; + /** Recreate table + * @param string original name + * @param string new name + * @param array [process_field()], empty to preserve + * @param array [$original => idf_escape($new_column)], empty to preserve + * @param string [format_foreign_key()], empty to preserve + * @param int set auto_increment to this value, 0 to preserve + * @param array [array($type, $name, $columns)], empty to preserve + * @param string CHECK constraint to drop + * @param string CHECK constraint to add + * @return bool + */ + function recreate_table($table, $name, $fields, $originals, $foreign, $auto_increment = 0, $indexes = array(), $drop_check = "", $add_check = "") { + global $connection, $driver; if ($table != "") { if (!$fields) { foreach (fields($table) as $key => $field) { @@ -585,6 +602,14 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { $fields[$key] = " " . implode($field); } $fields = array_merge($fields, array_filter($foreign)); + foreach ($driver->checkConstraints($table) as $check) { + if ($check != $drop_check) { + $fields[] = " CHECK ($check)"; + } + } + if ($add_check) { + $fields[] = " CHECK ($add_check)"; + } $temp_name = ($table == $name ? "adminer_$name" : $name); if (!queries("CREATE TABLE " . table($temp_name) . " (\n" . implode(",\n", $fields) . "\n)")) { // implicit ROLLBACK to not overwrite $connection->error @@ -785,7 +810,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { } function support($feature) { - return preg_match('~^(columns|database|drop_col|dump|indexes|descidx|move_col|sql|status|table|trigger|variables|view|view_trigger)$~', $feature); + return preg_match('~^(check|columns|database|drop_col|dump|indexes|descidx|move_col|sql|status|table|trigger|variables|view|view_trigger)$~', $feature); } function driver_config() { diff --git a/adminer/include/driver.inc.php b/adminer/include/driver.inc.php index 394ab635..9bb21e35 100644 --- a/adminer/include/driver.inc.php +++ b/adminer/include/driver.inc.php @@ -200,4 +200,18 @@ function get_driver($id) { return false; } + /** Get defined check constraints + * @param string + * @return array [$name => $clause] + */ + function checkConstraints($table) { + // MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not + return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE +FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c +JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME +WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . " +AND t.TABLE_NAME = " . q($table) . " +AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'"); // ignore default IS NOT NULL checks in PostrgreSQL + } + } diff --git a/adminer/include/editing.inc.php b/adminer/include/editing.inc.php index 53c9b159..33766988 100644 --- a/adminer/include/editing.inc.php +++ b/adminer/include/editing.inc.php @@ -531,20 +531,6 @@ function create_routine($routine, $row) { ; } -/** Get defined check constraints -* @param string -* @return array [$name => $clause] -*/ -function check_constraints($table) { - // MariaDB contains CHECK_CONSTRAINTS.TABLE_NAME, MySQL and PostrgreSQL not - return get_key_vals("SELECT c.CONSTRAINT_NAME, CHECK_CLAUSE -FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS c -JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS t ON c.CONSTRAINT_SCHEMA = t.CONSTRAINT_SCHEMA AND c.CONSTRAINT_NAME = t.CONSTRAINT_NAME -WHERE c.CONSTRAINT_SCHEMA = " . q($_GET["ns"] != "" ? $_GET["ns"] : DB) . " -AND t.TABLE_NAME = " . q($table) . " -AND CHECK_CLAUSE NOT LIKE '% IS NOT NULL'"); // ignore default IS NOT NULL checks in PostrgreSQL -} - /** Remove current user definer from SQL command * @param string * @return string diff --git a/adminer/table.inc.php b/adminer/table.inc.php index 76f1d5cb..d2af40c2 100644 --- a/adminer/table.inc.php +++ b/adminer/table.inc.php @@ -60,7 +60,7 @@ if (!is_view($table_status)) { if (support("check")) { echo "

" . lang('Checks') . "

\n"; - $check_constraints = check_constraints($TABLE); + $check_constraints = $driver->checkConstraints($TABLE); if ($check_constraints) { echo "\n"; foreach ($check_constraints as $key => $val) { diff --git a/changes.txt b/changes.txt index 08adefbb..c36a6222 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,5 @@ Adminer-4.17.2-dev: +SQLite: Support CHECK constraint SQLite: Add command Check tables SQLite: Display all rows of variable values MongoDB: Remove support for deprecated extension mongo diff --git a/compile.php b/compile.php index e3cd1f56..80b18969 100755 --- a/compile.php +++ b/compile.php @@ -67,7 +67,6 @@ header("Cache-Control: immutable"); ); //! respect context (extension, class) $functions = array_combine($matches[1], $matches[0]); $requires = array( - "check" => array("check_constraints"), "copy" => array("copy_tables"), "database" => array("create_database", "rename_database", "drop_databases"), "dump" => array("use_sql", "create_sql", "truncate_sql", "trigger_sql"),