diff --git a/adminer/drivers/mssql.inc.php b/adminer/drivers/mssql.inc.php index 2b541e62..85545b69 100644 --- a/adminer/drivers/mssql.inc.php +++ b/adminer/drivers/mssql.inc.php @@ -233,6 +233,30 @@ if (isset($_GET["mssql"])) { } + + + class Min_Driver extends Min_SQL { + + function insertUpdate($table, $set, $primary) { + $update = array(); + $where = array(); + foreach ($set as $key => $val) { + $update[] = "$key = $val"; + if (isset($primary[idf_unescape($key)])) { + $where[] = "$key = $val"; + } + } + // can use only one query for all rows with different API + return queries("MERGE " . table($table) . " USING (VALUES(" . implode(", ", $set) . ")) AS source (c" . implode(", c", range(1, count($set))) . ") ON " . implode(" AND ", $where) //! source, c1 - possible conflict + . " WHEN MATCHED THEN UPDATE SET " . implode(", ", $update) + . " WHEN NOT MATCHED THEN INSERT (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ");" // ; is mandatory + ); + } + + } + + + function idf_escape($idf) { return "[" . str_replace("]", "]]", $idf) . "]"; } @@ -460,26 +484,6 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table) return queries("BEGIN TRANSACTION"); } - function insert_into($table, $set) { - return queries("INSERT INTO " . table($table) . ($set ? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")" : "DEFAULT VALUES")); - } - - function insert_update($table, $set, $primary) { - $update = array(); - $where = array(); - foreach ($set as $key => $val) { - $update[] = "$key = $val"; - if (isset($primary[idf_unescape($key)])) { - $where[] = "$key = $val"; - } - } - // can use only one query for all rows with different API - return queries("MERGE " . table($table) . " USING (VALUES(" . implode(", ", $set) . ")) AS source (c" . implode(", c", range(1, count($set))) . ") ON " . implode(" AND ", $where) //! source, c1 - possible conflict - . " WHEN MATCHED THEN UPDATE SET " . implode(", ", $update) - . " WHEN NOT MATCHED THEN INSERT (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ");" // ; is mandatory - ); - } - function last_id() { global $connection; return $connection->result("SELECT SCOPE_IDENTITY()"); // @@IDENTITY can return trigger INSERT diff --git a/adminer/drivers/mysql.inc.php b/adminer/drivers/mysql.inc.php index 772e3ce4..21117f7b 100644 --- a/adminer/drivers/mysql.inc.php +++ b/adminer/drivers/mysql.inc.php @@ -229,6 +229,26 @@ if (!defined("DRIVER")) { } + + + class Min_Driver extends Min_SQL { + + function insert($table, $set) { + return ($set ? parent::insert($table, $set) : queries("INSERT INTO " . table($table) . " ()\nVALUES ()")); + } + + function insertUpdate($table, $set, $primary) { + foreach ($set as $key => $val) { + $set[$key] = "$key = $val"; + } + $update = implode(", ", $set); + return queries("INSERT INTO " . table($table) . " SET $update ON DUPLICATE KEY UPDATE $update"); + } + + } + + + /** Escape database identifier * @param string * @return string @@ -807,29 +827,6 @@ if (!defined("DRIVER")) { return queries("BEGIN"); } - /** Insert data into table - * @param string - * @param array - * @return bool - */ - function insert_into($table, $set) { - return queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")"); - } - - /** Insert or update data in the table - * @param string - * @param array - * @param array columns in keys - * @return bool - */ - function insert_update($table, $set, $primary) { - foreach ($set as $key => $val) { - $set[$key] = "$key = $val"; - } - $update = implode(", ", $set); - return queries("INSERT INTO " . table($table) . " SET $update ON DUPLICATE KEY UPDATE $update"); - } - /** Get last auto increment ID * @return string */ diff --git a/adminer/drivers/oracle.inc.php b/adminer/drivers/oracle.inc.php index 4422e20d..c5a30f1e 100644 --- a/adminer/drivers/oracle.inc.php +++ b/adminer/drivers/oracle.inc.php @@ -132,6 +132,16 @@ if (isset($_GET["oracle"])) { } + + + class Min_Driver extends Min_SQL { + + //! support empty $set in insert() + + } + + + function idf_escape($idf) { return '"' . str_replace('"', '""', $idf) . '"'; } @@ -322,10 +332,6 @@ ORDER BY uc.constraint_type, uic.column_position", $connection2) as $row) { return true; // automatic start } - function insert_into($table, $set) { - return queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")"); //! no columns - } - function last_id() { return 0; //! } diff --git a/adminer/drivers/pgsql.inc.php b/adminer/drivers/pgsql.inc.php index 2e46e28b..41341eac 100644 --- a/adminer/drivers/pgsql.inc.php +++ b/adminer/drivers/pgsql.inc.php @@ -148,7 +148,30 @@ if (isset($_GET["pgsql"])) { } } - + + + + class Min_Driver extends Min_SQL { + + function insertUpdate($table, $set, $primary) { + global $connection; + $update = array(); + $where = array(); + foreach ($set as $key => $val) { + $update[] = "$key = $val"; + if (isset($primary[idf_unescape($key)])) { + $where[] = "$key = $val"; + } + } + return ($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $connection->affected_rows) + || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")") + ; + } + + } + + + function idf_escape($idf) { return '"' . str_replace('"', '""', $idf) . '"'; } @@ -515,25 +538,6 @@ ORDER BY p.proname'); return queries("BEGIN"); } - function insert_into($table, $set) { - return queries("INSERT INTO " . table($table) . ($set ? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")" : "DEFAULT VALUES")); - } - - function insert_update($table, $set, $primary) { - global $connection; - $update = array(); - $where = array(); - foreach ($set as $key => $val) { - $update[] = "$key = $val"; - if (isset($primary[idf_unescape($key)])) { - $where[] = "$key = $val"; - } - } - return ($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $connection->affected_rows) - || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")") - ; - } - function last_id() { return 0; // there can be several sequences } diff --git a/adminer/drivers/sqlite.inc.php b/adminer/drivers/sqlite.inc.php index 64b75d8d..888fa9e0 100644 --- a/adminer/drivers/sqlite.inc.php +++ b/adminer/drivers/sqlite.inc.php @@ -203,7 +203,19 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { } } } - + + + + class Min_Driver extends Min_SQL { + + function insertUpdate($table, $set, $primary) { + return queries("REPLACE INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"); + } + + } + + + function idf_escape($idf) { return '"' . str_replace('"', '""', $idf) . '"'; } @@ -587,14 +599,6 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) { return queries("BEGIN"); } - function insert_into($table, $set) { - return queries("INSERT INTO " . table($table) . ($set ? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")" : "DEFAULT VALUES")); - } - - function insert_update($table, $set, $primary) { - return queries("REPLACE INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")"); - } - function last_id() { global $connection; return $connection->result("SELECT LAST_INSERT_ROWID()"); diff --git a/adminer/dump.inc.php b/adminer/dump.inc.php index 4ad663a3..c5c3f47c 100644 --- a/adminer/dump.inc.php +++ b/adminer/dump.inc.php @@ -126,7 +126,7 @@ page_header(lang('Export'), $error, ($_GET["export"] != "" ? array("table" => $_ $db_style = array('', 'USE', 'DROP+CREATE', 'CREATE'); $table_style = array('', 'DROP+CREATE', 'CREATE'); $data_style = array('', 'TRUNCATE+INSERT', 'INSERT'); -if ($jush == "sql") { //! use insert_update() in all drivers +if ($jush == "sql") { //! use insertUpdate() in all drivers $data_style[] = 'INSERT+UPDATE'; } parse_str($_COOKIE["adminer_export"], $row); diff --git a/adminer/edit.inc.php b/adminer/edit.inc.php index d63e1534..4bcb240a 100644 --- a/adminer/edit.inc.php +++ b/adminer/edit.inc.php @@ -22,12 +22,12 @@ if ($_POST && !$error && !isset($_GET["select"])) { $query_where = "\nWHERE $where"; if (isset($_POST["delete"])) { - $query = "FROM " . table($TABLE); - query_redirect( - "DELETE" . ($unique_array ? " $query$query_where" : limit1($query, $query_where)), + queries_redirect( $location, - lang('Item has been deleted.') + lang('Item has been deleted.'), + $driver->delete($TABLE, $query_where, !$unique_array) ); + } else { $set = array(); foreach ($fields as $name => $field) { @@ -53,7 +53,7 @@ if ($_POST && !$error && !isset($_GET["select"])) { exit; } } else { - $result = insert_into($TABLE, $set); + $result = $driver->insert($TABLE, $set); $last_id = ($result ? last_id() : 0); queries_redirect($location, lang('Item%s has been inserted.', ($last_id ? " $last_id" : "")), $result); //! link } diff --git a/adminer/include/auth.inc.php b/adminer/include/auth.inc.php index 14070be4..260b377c 100644 --- a/adminer/include/auth.inc.php +++ b/adminer/include/auth.inc.php @@ -116,6 +116,8 @@ if (is_string($connection) || !$adminer->login($_GET["username"], get_session("p exit; } +$driver = new Min_Driver($connection); + $token = $_SESSION["token"]; ///< @var string CSRF protection if ($auth && $_POST["token"]) { $_POST["token"] = $token; // reset token after explicit login diff --git a/adminer/include/bootstrap.inc.php b/adminer/include/bootstrap.inc.php index c71a851a..3bb2f2e9 100644 --- a/adminer/include/bootstrap.inc.php +++ b/adminer/include/bootstrap.inc.php @@ -58,6 +58,7 @@ if (function_exists("set_magic_quotes_runtime")) { // removed in PHP 6 include "../adminer/include/lang.inc.php"; include "../adminer/lang/$LANG.inc.php"; include "../adminer/include/pdo.inc.php"; +include "../adminer/include/driver.inc.php"; include "../adminer/drivers/sqlite.inc.php"; include "../adminer/drivers/pgsql.inc.php"; include "../adminer/drivers/oracle.inc.php"; diff --git a/adminer/include/driver.inc.php b/adminer/include/driver.inc.php new file mode 100644 index 00000000..5321a066 --- /dev/null +++ b/adminer/include/driver.inc.php @@ -0,0 +1,43 @@ +_conn = $connection; + } + + /** Delete data from table + * @param string + * @param string " WHERE ..." + * @param int 0 or 1 + * @return bool + */ + function delete($table, $queryWhere, $limit = 0) { + $query = "FROM " . table($table); + return queries("DELETE" . ($limit ? limit1($query, $queryWhere) : " $query$queryWhere")); + } + + /** Insert data into table + * @param string + * @param array + * @return bool + */ + function insert($table, $set) { + return queries("INSERT INTO " . table($table) . ($set ? " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")" : "DEFAULT VALUES")); + } + + /** Insert or update data in table + * @param string + * @param array + * @param array columns in keys + * @return bool + */ + /*abstract*/ function insertUpdate($table, $set, $primary) { + return false; + } + +} diff --git a/adminer/select.inc.php b/adminer/select.inc.php index c8dc5ec7..84ea1e32 100644 --- a/adminer/select.inc.php +++ b/adminer/select.inc.php @@ -103,21 +103,24 @@ if ($_POST && !$error) { } if ($_POST["delete"] || $set) { $command = "UPDATE"; - if ($_POST["delete"]) { - $command = "DELETE"; - $query = "FROM $query"; - } if ($_POST["clone"]) { $command = "INSERT"; $query = "INTO $query"; } if ($_POST["all"] || ($unselected === array() && is_array($_POST["check"])) || $is_group) { - $result = queries("$command $query$where_check"); + $result = ($_POST["delete"] + ? $driver->delete($TABLE, $where_check) + : queries("$command $query$where_check") + ); $affected = $connection->affected_rows; } else { foreach ((array) $_POST["check"] as $val) { // where is not unique so OR can't be used - $result = queries($command . limit1($query, "\nWHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($val, $fields))); + $where2 = "\nWHERE " . ($where ? implode(" AND ", $where) . " AND " : "") . where_check($val, $fields); + $result = ($_POST["delete"] + ? $driver->delete($TABLE, $where2, 1) + : queries($command . limit1($query, $where2)) + ); if (!$result) { break; } @@ -181,7 +184,7 @@ if ($_POST && !$error) { foreach ($matches2[1] as $i => $col) { $set[idf_escape($cols[$i])] = ($col == "" && $fields[$cols[$i]]["null"] ? "NULL" : q(str_replace('""', '"', preg_replace('~^"|"$~', '', $col)))); } - $result = insert_update($TABLE, $set, $primary); + $result = $driver->insertUpdate($TABLE, $set, $primary); if (!$result) { break; } diff --git a/compile.php b/compile.php index 71d2792f..9471c11a 100755 --- a/compile.php +++ b/compile.php @@ -327,7 +327,7 @@ if ($_SERVER["argv"][1]) { $filename = dirname(__FILE__) . "/adminer/drivers/mysql.inc.php"; preg_match_all('~\\bfunction ([^(]+)~', file_get_contents($filename), $matches); //! respect context (extension, class) $functions = array_combine($matches[1], $matches[0]); -unset($functions["__destruct"], $functions["Min_DB"], $functions["Min_Result"]); +unset($functions["__destruct"], $functions["Min_DB"], $functions["Min_Result"], $functions["Min_Driver"]); foreach (glob(dirname(__FILE__) . "/adminer/drivers/" . ($driver ? $driver : "*") . ".inc.php") as $filename) { if ($filename != "mysql.inc.php") { $file = file_get_contents($filename); @@ -340,6 +340,7 @@ foreach (glob(dirname(__FILE__) . "/adminer/drivers/" . ($driver ? $driver : "*" } include dirname(__FILE__) . "/adminer/include/pdo.inc.php"; +include dirname(__FILE__) . "/adminer/include/driver.inc.php"; $features = array("call" => "routine", "dump", "event", "privileges", "procedure" => "routine", "processlist", "routine", "scheme", "sequence", "status", "trigger", "type", "user" => "privileges", "variables", "view"); $lang_ids = array(); // global variable simplifies usage in a callback function $file = file_get_contents(dirname(__FILE__) . "/$project/index.php");