]> git.joonet.de Git - adminer.git/commitdiff
Reintegrate sqlite branch
authorjakubvrana <jakubvrana@7c3ca157-0c34-0410-bff1-cbf682f78f5c>
Wed, 21 Apr 2010 12:01:32 +0000 (12:01 +0000)
committerjakubvrana <jakubvrana@7c3ca157-0c34-0410-bff1-cbf682f78f5c>
Wed, 21 Apr 2010 12:01:32 +0000 (12:01 +0000)
git-svn-id: https://adminer.svn.sourceforge.net/svnroot/adminer/trunk@1466 7c3ca157-0c34-0410-bff1-cbf682f78f5c

84 files changed:
adminer/call.inc.php
adminer/create.inc.php
adminer/database.inc.php
adminer/db.inc.php
adminer/download.inc.php
adminer/drivers/mssql.inc.php [new file with mode: 0644]
adminer/drivers/mysql.inc.php [new file with mode: 0644]
adminer/drivers/pgsql.inc.php [new file with mode: 0644]
adminer/drivers/sqlite.inc.php [new file with mode: 0644]
adminer/dump.inc.php
adminer/edit.inc.php
adminer/foreign.inc.php
adminer/include/adminer.inc.php
adminer/include/auth.inc.php
adminer/include/bootstrap.inc.php
adminer/include/connect.inc.php
adminer/include/design.inc.php
adminer/include/editing.inc.php
adminer/include/export.inc.php
adminer/include/functions.inc.php
adminer/include/mysql.inc.php [deleted file]
adminer/include/pdo.inc.php
adminer/include/version.inc.php
adminer/index.php
adminer/indexes.inc.php
adminer/lang/cs.inc.php
adminer/lang/de.inc.php
adminer/lang/es.inc.php
adminer/lang/et.inc.php
adminer/lang/fr.inc.php
adminer/lang/it.inc.php
adminer/lang/nl.inc.php
adminer/lang/ru.inc.php
adminer/lang/sk.inc.php
adminer/lang/zh-tw.inc.php
adminer/lang/zh.inc.php
adminer/privileges.inc.php
adminer/schema.inc.php
adminer/select.inc.php
adminer/sql.inc.php
adminer/static/default.css
adminer/static/editing.js
adminer/static/favicon.ico
adminer/static/functions.js
adminer/table.inc.php
adminer/trigger.inc.php
adminer/user.inc.php
adminer/view.inc.php
changes.txt
compile.php
coverage.php
editor/db.inc.php
editor/include/adminer.inc.php
editor/include/editing.inc.php
editor/index.php
lang.php
tests/0-login.html
tests/1-create-database.html
tests/10-clone.html
tests/11-reference.html
tests/12-update.html
tests/13-delete.html
tests/14-truncate.html
tests/15-privileges.html
tests/16-processlist.html
tests/17-export.html
tests/18-events.html
tests/19-procedures.html
tests/2-create-table.html
tests/20-partitioning.html
tests/21-variables.html
tests/22-history.html
tests/23-editor.html
tests/24-explain.html
tests/3-create-index.html
tests/4-create-table-2.html
tests/5-foreign-key.html
tests/6-alter-table.html
tests/7-create-trigger.html
tests/8-create-view.html
tests/9-insert.html
tests/logout.html
todo.txt
version.js

index 039868d82bc821f7d8759f33f808cd6a83d31436..2c781968180aa6b54cd6520ed39e4d6783836467 100644 (file)
@@ -28,7 +28,9 @@ if (!$error && $_POST) {
                }
                $call[] = (isset($out[$key]) ? "@" . idf_escape($field["field"]) : $val);
        }
-       if (!$connection->multi_query((isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . idf_escape($PROCEDURE) . "(" . implode(", ", $call) . ")")) {
+       $query = (isset($_GET["callf"]) ? "SELECT" : "CALL") . " " . idf_escape($PROCEDURE) . "(" . implode(", ", $call) . ")";
+       echo "<p><code class='jush-$driver'>" . h($query) . "</code> <a href='" . h(ME) . "sql=" . urlencode($query) . "'>" . lang('Edit') . "</a>\n";
+       if (!$connection->multi_query($query)) {
                echo "<p class='error'>" . error() . "\n";
        } else {
                do {
@@ -52,8 +54,9 @@ if ($in) {
        echo "<table cellspacing='0'>\n";
        foreach ($in as $key) {
                $field = $routine["fields"][$key];
-               echo "<tr><th>" . h($field["field"]);
-               $value = $_POST["fields"][$key];
+               $name = $field["field"];
+               echo "<tr><th>" . h($name);
+               $value = $_POST["fields"][$name];
                if ($value != "" && ereg("enum|set", $field["type"])) {
                        $value = intval($value);
                }
index e9fd1d0dd9b16492bc438ffb0c4b988ebafe795b..b1d4d4b974c966568f21c3c0cf93817da983c2bb 100644 (file)
@@ -19,20 +19,8 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"] && !$_POST["up"]
        if ($_POST["drop"]) {
                query_redirect("DROP TABLE " . idf_escape($_GET["create"]), substr(ME, 0, -1), lang('Table has been dropped.'));
        } else {
-               $auto_increment_index = " PRIMARY KEY";
-               // don't overwrite primary key by auto_increment
-               if ($TABLE != "" && $_POST["auto_increment_col"]) {
-                       foreach (indexes($TABLE) as $index) {
-                               if (in_array($_POST["fields"][$_POST["auto_increment_col"]]["orig"], $index["columns"], true)) {
-                                       $auto_increment_index = "";
-                                       break;
-                               }
-                               if ($index["type"] == "PRIMARY") {
-                                       $auto_increment_index = " UNIQUE";
-                               }
-                       }
-               }
-               $fields = "";
+               $fields = array();
+               $foreign = array();
                ksort($_POST["fields"]);
                $orig_field = reset($orig_fields);
                $after = "FIRST";
@@ -48,33 +36,26 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"] && !$_POST["up"]
                                                $field["on_update"] = "CURRENT_TIMESTAMP";
                                                $field["default"] = $default;
                                        }
+                                       if ($key == $_POST["auto_increment_col"]) {
+                                               $field["auto_increment"] = true;
+                                       }
                                        $process_field = process_field($field, $type_field);
-                                       $auto_increment = ($key == $_POST["auto_increment_col"]);
-                                       if ($process_field != process_field($orig_field, $orig_field) || $orig_field["auto_increment"] != $auto_increment) {
-                                               $fields .= "\n" . ($TABLE != "" ? ($field["orig"] != "" ? "CHANGE " . idf_escape($field["orig"]) : "ADD") : " ")
-                                                       . " $process_field"
-                                                       . ($auto_increment ? " AUTO_INCREMENT$auto_increment_index" : "")
-                                                       . ($TABLE != "" ? " $after" : "") . ","
-                                               ;
+                                       if ($process_field != process_field($orig_field, $orig_field)) {
+                                               $fields[] = array($field["orig"], $process_field, $after);
                                        }
                                        if (!isset($types[$field["type"]])) {
-                                               $fields .= ($TABLE != "" ? "\nADD" : "") . " FOREIGN KEY (" . idf_escape($field["field"]) . ") REFERENCES " . idf_escape($foreign_keys[$field["type"]]) . " (" . idf_escape($type_field["field"]) . "),";
+                                               $foreign[] = ($TABLE != "" ? "ADD " : "  ") . "FOREIGN KEY (" . idf_escape($field["field"]) . ") REFERENCES " . idf_escape($foreign_keys[$field["type"]]) . " (" . idf_escape($type_field["field"]) . ")";
                                        }
                                }
                                $after = "AFTER " . idf_escape($field["field"]);
-                               //! drop and create foreign keys with renamed columns
                        } elseif ($field["orig"] != "") {
-                               $fields .= "\nDROP " . idf_escape($field["orig"]) . ",";
+                               $fields[] = array($field["orig"]);
                        }
                        if ($field["orig"] != "") {
                                $orig_field = next($orig_fields);
                        }
                }
-               $status = "COMMENT=" . $connection->quote($_POST["Comment"])
-                       . ($_POST["Engine"] && $_POST["Engine"] != $orig_status["Engine"] ? " ENGINE=" . $connection->quote($_POST["Engine"]) : "")
-                       . ($_POST["Collation"] && $_POST["Collation"] != $orig_status["Collation"] ? " COLLATE " . $connection->quote($_POST["Collation"]) : "")
-                       . ($_POST["Auto_increment"] != "" ? " AUTO_INCREMENT=" . preg_replace('~[^0-9]+~', '', $_POST["Auto_increment"]) : "")
-               ;
+               $partitioning = "";
                if (in_array($_POST["partition_by"], $partition_by)) {
                        $partitions = array();
                        if ($_POST["partition_by"] == 'RANGE' || $_POST["partition_by"] == 'LIST') {
@@ -83,20 +64,29 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"] && !$_POST["up"]
                                        $partitions[] = "\nPARTITION " . idf_escape($val) . " VALUES " . ($_POST["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
                                }
                        }
-                       $status .= "\nPARTITION BY $_POST[partition_by]($_POST[partition])" . ($partitions // $_POST["partition"] can be expression, not only column
+                       $partitioning .= "\nPARTITION BY $_POST[partition_by]($_POST[partition])" . ($partitions // $_POST["partition"] can be expression, not only column
                                ? " (" . implode(",", $partitions) . "\n)"
                                : ($_POST["partitions"] ? " PARTITIONS " . intval($_POST["partitions"]) : "")
                        );
-               } elseif ($connection->server_info >= 5.1 && $TABLE != "") {
-                       $status .= "\nREMOVE PARTITIONING";
+               } elseif ($TABLE != "") {
+                       $partitioning .= "\nREMOVE PARTITIONING";
                }
-               $location = ME . "table=" . urlencode($_POST["name"]);
-               if ($TABLE != "") {
-                       query_redirect("ALTER TABLE " . idf_escape($TABLE) . "$fields\nRENAME TO " . idf_escape($_POST["name"]) . ",\n$status", $location, lang('Table has been altered.'));
-               } else {
+               $message = lang('Table has been altered.');
+               if ($TABLE == "") {
                        cookie("adminer_engine", $_POST["Engine"]);
-                       query_redirect("CREATE TABLE " . idf_escape($_POST["name"]) . " (" . substr($fields, 0, -1) . "\n) $status", $location, lang('Table has been created.'));
+                       $message = lang('Table has been created.');
                }
+               queries_redirect(ME . "table=" . urlencode($_POST["name"]), $message, alter_table(
+                       $TABLE,
+                       $_POST["name"],
+                       $fields,
+                       $foreign,
+                       $_POST["Comment"],
+                       ($_POST["Engine"] && $_POST["Engine"] != $orig_status["Engine"] ? $_POST["Engine"] : ""),
+                       ($_POST["Collation"] && $_POST["Collation"] != $orig_status["Collation"] ? $_POST["Collation"] : ""),
+                       ($_POST["Auto_increment"] != "" ? preg_replace('~[^0-9]+~', '', $_POST["Auto_increment"]) : ""),
+                       $partitioning
+               ));
        }
 }
 
@@ -127,9 +117,9 @@ if ($_POST) {
                }
                $row["fields"][] = $field;
        }
-       if ($connection->server_info >= 5.1) {
+       if (support("partitioning")) {
                $from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . $connection->quote(DB) . " AND TABLE_NAME = " . $connection->quote($TABLE);
-               $result = $connection->query("SELECT PARTITION_METHOD, PARTITION_ORDINAL_POSITION, PARTITION_EXPRESSION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
+               $result = $connection->query("SELECT" . limit("PARTITION_METHOD, PARTITION_ORDINAL_POSITION, PARTITION_EXPRESSION $from ORDER BY PARTITION_ORDINAL_POSITION", 1));
                list($row["partition_by"], $row["partitions"], $row["partition"]) = $result->fetch_row();
                $row["partition_names"] = array();
                $row["partition_values"] = array();
@@ -162,24 +152,23 @@ foreach ($engines as $engine) {
 <p>
 <?php echo lang('Table name'); ?>: <input name="name" maxlength="64" value="<?php echo h($row["name"]); ?>">
 <?php echo ($engines ? html_select("Engine", array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) : ""); ?>
- <?php echo html_select("Collation", array("" => "(" . lang('collation') . ")") + $collations, $row["Collation"]); ?>
+ <?php echo ($collations ? html_select("Collation", array("" => "(" . lang('collation') . ")") + $collations, $row["Collation"]) : ""); ?>
  <input type="submit" value="<?php echo lang('Save'); ?>">
 <table cellspacing="0" id="edit-fields" class="nowrap">
-<?php $column_comments = edit_fields($row["fields"], $collations, "TABLE", $suhosin, $foreign_keys); ?>
+<?php $comments = edit_fields($row["fields"], $collations, "TABLE", $suhosin, $foreign_keys, $row["Comment"] != ""); ?>
 </table>
 <p>
 <?php echo lang('Auto Increment'); ?>: <input name="Auto_increment" size="6" value="<?php echo h($row["Auto_increment"]); ?>">
-<?php echo lang('Comment'); ?>: <input name="Comment" value="<?php echo h($row["Comment"]); ?>" maxlength="60">
 <script type="text/javascript">
 document.write('<label><input type="checkbox" onclick="columnShow(this.checked, 5);"><?php echo lang('Default values'); ?><\/label>');
-document.write('<label><input type="checkbox"<?php if ($column_comments) { ?> checked<?php } ?> onclick="columnShow(this.checked, 6);"><?php echo lang('Show column comments'); ?><\/label>');
 </script>
+<?php echo (support("comment") ? checkbox("", "", $comments, lang('Comment'), "columnShow(this.checked, 6); toggle('Comment');") . ' <input id="Comment" name="Comment" value="' . h($row["Comment"]) . '" maxlength="60"' . ($comments ? '' : ' class="hidden"') . '>' : ''); ?>
 <p>
 <input type="hidden" name="token" value="<?php echo $token; ?>">
 <input type="submit" value="<?php echo lang('Save'); ?>">
 <?php if (strlen($_GET["create"])) { ?><input type="submit" name="drop" value="<?php echo lang('Drop'); ?>"<?php echo $confirm; ?>><?php } ?>
 <?php
-if ($connection->server_info >= 5.1) {
+if (support("partitioning")) {
        $partition_table = ereg('RANGE|LIST', $row["partition_by"]);
        print_fieldset("partition", lang('Partition by'), $row["partition_by"]);
        ?>
index 80e4b65e808e38aae99fde78074f76f8fcd04703..ece5915807ca6dff87ad60bf6a04264ec5189395 100644 (file)
@@ -2,35 +2,26 @@
 if ($_POST && !$error && !isset($_POST["add_x"])) { // add is an image and PHP changes add.x to add_x
        restart_session();
        if ($_POST["drop"]) {
-               unset($_SESSION["databases"][$_GET["server"]]);
+               set_session("databases", null);
                query_redirect("DROP DATABASE " . idf_escape(DB), remove_from_uri("db|database"), lang('Database has been dropped.'));
        } elseif (DB !== $_POST["name"]) {
                // create or rename database
-               unset($_SESSION["databases"][$_GET["server"]]); // clear cache
-               $dbs = explode("\n", str_replace("\r", "", $_POST["name"]));
-               $failed = false;
-               $last = "";
-               foreach ($dbs as $db) {
-                       if (count($dbs) == 1 || $db != "") { // ignore empty lines but always try to create single database
-                               if (!queries("CREATE DATABASE " . idf_escape($db) . ($_POST["collation"] ? " COLLATE " . $connection->quote($_POST["collation"]) : ""))) {
-                                       $failed = true;
+               set_session("databases", null); // clear cache
+               if (DB != "") {
+                       queries_redirect(preg_replace('~db=[^&]*&~', '', ME) . "db=" . urlencode($_POST["name"]), lang('Database has been renamed.'), rename_database($_POST["name"], $_POST["collation"]));
+               } else {
+                       $dbs = explode("\n", str_replace("\r", "", $_POST["name"]));
+                       $success = true;
+                       $last = "";
+                       foreach ($dbs as $db) {
+                               if (count($dbs) == 1 || $db != "") { // ignore empty lines but always try to create single database
+                                       if (!queries("CREATE DATABASE " . idf_escape($db) . ($_POST["collation"] ? " COLLATE " . $connection->quote($_POST["collation"]) : ""))) {
+                                               $success = false;
+                                       }
+                                       $last = $db;
                                }
-                               $last = $db;
                        }
-               }
-               if (query_redirect(queries(), ME . "db=" . urlencode($last), lang('Database has been created.'), DB == "", false, $failed)) {
-                       //! move triggers
-                       $result = $connection->query("SHOW TABLES");
-                       while ($row = $result->fetch_row()) {
-                               if (!queries("RENAME TABLE " . idf_escape($row[0]) . " TO " . idf_escape($_POST["name"]) . "." . idf_escape($row[0]))) {
-                                       break;
-                               }
-                       }
-                       if (!$row) {
-                               queries("DROP DATABASE " . idf_escape(DB));
-                               //! saved to history of removed database
-                       }
-                       queries_redirect(preg_replace('~db=[^&]*&~', '', ME) . "db=" . urlencode($_POST["name"]), lang('Database has been renamed.'), !$row);
+                       queries_redirect(ME . "db=" . urlencode($last), lang('Database has been created.'), $success);
                }
        } else {
                // alter database
@@ -49,17 +40,16 @@ $collate = null;
 if ($_POST) {
        $name = $_POST["name"];
        $collate = $_POST["collation"];
-} elseif (DB == "") {
+} elseif (DB != "") {
+       $collate = db_collation(DB, $collations);
+} elseif ($driver == "sql") {
        // propose database name with limited privileges
-       $result = $connection->query("SHOW GRANTS");
-       while ($row = $result->fetch_row()) {
-               if (preg_match('~ ON (`(([^\\\\`]|``|\\\\.)*)%`\\.\\*)?~', $row[0], $match) && $match[1]) {
-                       $name = stripcslashes(idf_unescape($match[2]));
+       foreach (get_vals("SHOW GRANTS") as $grant) {
+               if (preg_match('~ ON (`(([^\\\\`]|``|\\\\.)*)%`\\.\\*)?~', $grant, $match) && $match[1]) {
+                       $name = stripcslashes(idf_unescape("`$match[2]`"));
                        break;
                }
        }
-} else {
-       $collate = db_collation(DB, $collations);
 }
 ?>
 
index 6f22c207962c9da9f719465b6bf679ea06591be3..ef197347d3f865116e42e50eaadc3ca07409087c 100644 (file)
@@ -4,18 +4,15 @@ $tables_views = array_merge((array) $_POST["tables"], (array) $_POST["views"]);
 if ($tables_views && !$error && !$_POST["search"]) {
        $result = true;
        $message = "";
-       if (count($_POST["tables"]) > 1 && ($_POST["drop"] || $_POST["truncate"])) {
+       if ($driver == "sql" && count($_POST["tables"]) > 1 && ($_POST["drop"] || $_POST["truncate"])) {
                queries("SET foreign_key_checks = 0"); // allows to truncate or drop several tables at once
        }
-       if (isset($_POST["truncate"])) {
-               foreach ((array) $_POST["tables"] as $table) {
-                       if (!queries("TRUNCATE " . idf_escape($table))) {
-                               $result = false;
-                               break;
-                       }
+       if ($_POST["truncate"]) {
+               if ($_POST["tables"]) {
+                       $result = truncate_tables($_POST["tables"]);
                }
                $message = lang('Tables have been truncated.');
-       } elseif (isset($_POST["move"])) {
+       } elseif ($_POST["move"]) {
                $rename = array();
                foreach ($tables_views as $table) {
                        $rename[] = idf_escape($table) . " TO " . idf_escape($_POST["target"]) . "." . idf_escape($table);
@@ -23,66 +20,65 @@ if ($tables_views && !$error && !$_POST["search"]) {
                $result = queries("RENAME TABLE " . implode(", ", $rename));
                //! move triggers
                $message = lang('Tables have been moved.');
-       } elseif ((!isset($_POST["drop"]) || !$_POST["views"] || queries("DROP VIEW " . implode(", ", array_map('idf_escape', $_POST["views"]))))
-               && (!$_POST["tables"] || ($result = queries((isset($_POST["optimize"]) ? "OPTIMIZE" : (isset($_POST["check"]) ? "CHECK" : (isset($_POST["repair"]) ? "REPAIR" : (isset($_POST["drop"]) ? "DROP" : "ANALYZE")))) . " TABLE " . implode(", ", array_map('idf_escape', $_POST["tables"])))))
-       ) {
-               if (isset($_POST["drop"])) {
-                       $message = lang('Tables have been dropped.');
-               } else {
-                       while ($row = $result->fetch_assoc()) {
-                               $message .= h("$row[Table]: $row[Msg_text]") . "<br>";
-                       }
+       } elseif ($_POST["drop"]) {
+               if ($_POST["views"]) {
+                       $result = drop_views($_POST["views"]);
+               }
+               if ($result && $_POST["tables"]) {
+                       $result = drop_tables($_POST["tables"]);
+               }
+               $message = lang('Tables have been dropped.');
+       } elseif ($_POST["tables"] && ($result = queries(($_POST["optimize"] ? "OPTIMIZE" : ($_POST["check"] ? "CHECK" : ($_POST["repair"] ? "REPAIR" : "ANALYZE"))) . " TABLE " . implode(", ", array_map('idf_escape', $_POST["tables"]))))) {
+               while ($row = $result->fetch_assoc()) {
+                       $message .= h("$row[Table]: $row[Msg_text]") . "<br>";
                }
        }
        queries_redirect(substr(ME, 0, -1), $message, $result);
 }
 
-page_header(lang('Database') . ": " . h(DB), $error, false);
+page_header(lang('Database') . ": " . h(DB), $error, true);
 echo '<p><a href="' . h(ME) . 'database=">' . lang('Alter database') . "</a>\n";
 echo '<a href="' . h(ME) . 'schema=">' . lang('Database schema') . "</a>\n";
+$sums = array("Data_length" => 0, "Index_length" => 0, "Data_free" => 0);
 
 echo "<h3>" . lang('Tables and views') . "</h3>\n";
-$table_status = table_status();
-if (!$table_status) {
+$tables_list = tables_list();
+if (!$tables_list) {
        echo "<p class='message'>" . lang('No tables.') . "\n";
 } else {
        echo "<form action='' method='post'>\n";
        echo "<p><input name='query' value='" . h($_POST["query"]) . "'> <input type='submit' name='search' value='" . lang('Search') . "'>\n";
        if ($_POST["search"] && $_POST["query"] != "") {
-               $_GET["where"][0]["op"] = "LIKE";
-               $_GET["where"][0]["val"] = "%$_POST[query]%";
+               $_GET["where"][0]["op"] = "LIKE %%";
+               $_GET["where"][0]["val"] = $_POST["query"];
                search_tables();
        }
        echo "<table cellspacing='0' class='nowrap' onclick='tableClick(event);'>\n";
-       echo '<thead><tr class="wrap"><td><input id="check-all" type="checkbox" onclick="formCheck(this, /^(tables|views)\[/);"><th>' . lang('Table') . '<td>' . lang('Engine') . '<td>' . lang('Collation') . '<td>' . lang('Data Length') . '<td>' . lang('Index Length') . '<td>' . lang('Data Free') . '<td>' . lang('Auto Increment') . '<td>' . lang('Rows') . '<td>' . lang('Comment') . "</thead>\n";
-       $sums = array();
-       foreach ($table_status as $row) {
-               $name = $row["Name"];
-               echo '<tr' . odd() . '><td>' . checkbox((isset($row["Rows"]) ? "tables[]" : "views[]"), $name, in_array($name, $tables_views, true), "", "formUncheck('check-all');");
+       echo '<thead><tr class="wrap"><td><input id="check-all" type="checkbox" onclick="formCheck(this, /^(tables|views)\[/);"><th>' . lang('Table') . '<td>' . lang('Engine') . '<td>' . lang('Collation') . '<td>' . lang('Data Length') . '<td>' . lang('Index Length') . '<td>' . lang('Data Free') . '<td>' . lang('Auto Increment') . '<td>' . lang('Rows') . (support("comment") ? '<td>' . lang('Comment') : '') . "</thead>\n";
+       foreach ($tables_list as $name => $type) {
+               $view = (isset($type) && !eregi("table", $type));
+               echo '<tr' . odd() . '><td>' . checkbox(($view ? "views[]" : "tables[]"), $name, in_array($name, $tables_views, true), "", "formUncheck('check-all');");
                echo '<th><a href="' . h(ME) . 'table=' . urlencode($name) . '">' . h($name) . '</a>';
-               if (isset($row["Rows"])) {
-                       echo "<td>$row[Engine]<td>$row[Collation]";
-                       foreach (array("Data_length" => "create", "Index_length" => "indexes", "Data_free" => "edit", "Auto_increment" => "auto_increment=1&create", "Rows" => "select") as $key => $link) {
-                               $val = number_format($row[$key], 0, '.', lang(','));
-                               echo '<td align="right">' . ($row[$key] != "" ? '<a href="' . h(ME . "$link=") . urlencode($name) . '">' . str_replace(" ", "&nbsp;", ($key == "Rows" && $row["Engine"] == "InnoDB" && $val ? lang('~ %s', $val) : $val)) . '</a>' : '&nbsp;');
-                               $sums[$link] += ($row["Engine"] != "InnoDB" || $link != "edit" ? $row[$key] : 0);
-                       }
-                       echo "<td>" . nbsp($row["Comment"]);
-               } else {
+               if ($view) {
                        echo '<td colspan="6"><a href="' . h(ME) . "view=" . urlencode($name) . '">' . lang('View') . '</a>';
                        echo '<td align="right"><a href="' . h(ME) . "select=" . urlencode($name) . '">?</a>';
-                       echo '<td>&nbsp;';
+               } else {
+                       echo "<td id='Engine-" . h($name) . "'>&nbsp;<td id='Collation-" . h($name) . "'>&nbsp;";
+                       foreach (array("Data_length" => "create", "Index_length" => "indexes", "Data_free" => "edit", "Auto_increment" => "auto_increment=1&create", "Rows" => "select") as $key => $link) {
+                               echo "<td align='right'><a href='" . h(ME . "$link=") . urlencode($name) . "' id='$key-" . h($name) . "'>?</a>";
+                       }
                }
+               echo (support("comment") ? "<td id='Comment-" . h($name) . "'>&nbsp;" : "");
        }
-       echo "<tr><td>&nbsp;<th>" . lang('%d in total', count($table_status));
-       echo "<td>" . $connection->result($connection->query("SELECT @@storage_engine"));
+       echo "<tr><td>&nbsp;<th>" . lang('%d in total', count($tables_list));
+       echo "<td>" . $connection->result("SELECT @@storage_engine");
        echo "<td>" . db_collation(DB, collations());
-       foreach (array("create", "indexes", "edit") as $val) {
-               echo "<td align='right'>" . number_format($sums[$val], 0, '.', lang(','));
+       foreach ($sums as $key => $val) {
+               echo "<td align='right' id='sum-$key'>&nbsp;";
        }
        echo "</table>\n";
        if (!information_schema(DB)) {
-               echo "<p><input type='hidden' name='token' value='$token'><input type='submit' value='" . lang('Analyze') . "'> <input type='submit' name='optimize' value='" . lang('Optimize') . "'> <input type='submit' name='check' value='" . lang('Check') . "'> <input type='submit' name='repair' value='" . lang('Repair') . "'> <input type='submit' name='truncate' value='" . lang('Truncate') . "' onclick=\"return confirm('" . lang('Are you sure?') . " (' + formChecked(this, /tables/) + ')');\"> <input type='submit' name='drop' value='" . lang('Drop') . "' onclick=\"return confirm('" . lang('Are you sure?') . " (' + formChecked(this, /tables|views/) + ')');\">\n";
+               echo "<p><input type='hidden' name='token' value='$token'>" . ($driver == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> <input type='submit' name='optimize' value='" . lang('Optimize') . "'> <input type='submit' name='check' value='" . lang('Check') . "'> <input type='submit' name='repair' value='" . lang('Repair') . "'> " : "") . "<input type='submit' name='truncate' value='" . lang('Truncate') . "' onclick=\"return confirm('" . lang('Are you sure?') . " (' + formChecked(this, /tables/) + ')');\"> <input type='submit' name='drop' value='" . lang('Drop') . "' onclick=\"return confirm('" . lang('Are you sure?') . " (' + formChecked(this, /tables|views/) + ')');\">\n";
                $dbs = get_databases();
                if (count($dbs) != 1) {
                        $db = (isset($_POST["target"]) ? $_POST["target"] : DB);
@@ -93,8 +89,10 @@ if (!$table_status) {
 }
 
 echo '<p><a href="' . h(ME) . 'create=">' . lang('Create table') . "</a>\n";
-if ($connection->server_info >= 5) {
+if (support("view")) {
        echo '<a href="' . h(ME) . 'view=">' . lang('Create view') . "</a>\n";
+}
+if (support("routine")) {
        echo "<h3>" . lang('Routines') . "</h3>\n";
        $result = $connection->query("SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = " . $connection->quote(DB));
        if ($result->num_rows) {
@@ -110,9 +108,10 @@ if ($connection->server_info >= 5) {
        echo '<p><a href="' . h(ME) . 'procedure=">' . lang('Create procedure') . '</a> <a href="' . h(ME) . 'function=">' . lang('Create function') . "</a>\n";
 }
 
-if ($connection->server_info >= 5.1 && ($result = $connection->query("SHOW EVENTS"))) {
+if (support("event")) {
        echo "<h3>" . lang('Events') . "</h3>\n";
-       if ($result->num_rows) {
+       $result = $connection->query("SHOW EVENTS");
+       if ($result && $result->num_rows) {
                echo "<table cellspacing='0'>\n";
                echo "<thead><tr><th>" . lang('Name') . "<td>" . lang('Schedule') . "<td>" . lang('Start') . "<td>" . lang('End') . "</thead>\n";
                while ($row = $result->fetch_assoc()) {
@@ -125,3 +124,34 @@ if ($connection->server_info >= 5.1 && ($result = $connection->query("SHOW EVENT
        }
        echo '<p><a href="' . h(ME) . 'event=">' . lang('Create event') . "</a>\n";
 }
+
+page_footer();
+$table_status = table_status();
+if ($table_status) {
+       echo "<script type='text/javascript'>\n";
+       foreach ($table_status as $row) {
+               $id = addcslashes($row["Name"], "\\'/");
+               echo "setHtml('Comment-$id', '" . nbsp($row["Comment"]) . "');\n";
+               if (!eregi("view", $row["Engine"])) {
+                       foreach (array("Engine", "Collation") as $key) {
+                               echo "setHtml('$key-$id', '" . nbsp($row[$key]) . "');\n";
+                       }
+                       foreach ($sums + array("Auto_increment" => 0, "Rows" => 0) as $key => $val) {
+                               if ($row[$key] != "") {
+                                       $val = number_format($row[$key], 0, '.', lang(','));
+                                       echo "setHtml('$key-$id', '" . ($key == "Rows" && $row["Engine"] == "InnoDB" && $val ? "~ $val" : $val) . "');\n";
+                                       if (isset($sums[$key])) {
+                                               $sums[$key] += ($row["Engine"] != "InnoDB" || $key != "Data_free" ? $row[$key] : 0);
+                                       }
+                               } elseif (array_key_exists($key, $row)) {
+                                       echo "setHtml('$key-$id');\n";
+                               }
+                       }
+               }
+       }
+       foreach ($sums as $key => $val) {
+               echo "setHtml('sum-$key', '" . number_format($val, 0, '.', lang(',')) . "');\n";
+       }
+       echo "</script>\n";
+}
+exit; // page_footer() already called
index dcf39415270597e3e798b064426d4795c9e6f1bb..faa28ce44e663172101f1cd2e429938bd126db65 100644 (file)
@@ -2,5 +2,5 @@
 $TABLE = $_GET["download"];
 header("Content-Type: application/octet-stream");
 header("Content-Disposition: attachment; filename=" . friendly_url("$TABLE-" . implode("_", $_GET["where"])) . "." . friendly_url($_GET["field"]));
-echo $connection->result($connection->query("SELECT " . idf_escape($_GET["field"]) . " FROM " . idf_escape($TABLE) . " WHERE " . where($_GET) . " LIMIT 1"));
+echo $connection->result("SELECT" . limit(idf_escape($_GET["field"]) . " FROM " . idf_escape($TABLE) . " WHERE " . where($_GET), 1));
 exit; // don't output footer
diff --git a/adminer/drivers/mssql.inc.php b/adminer/drivers/mssql.inc.php
new file mode 100644 (file)
index 0000000..98f38e9
--- /dev/null
@@ -0,0 +1,406 @@
+<?php
+/**
+* @author Jakub Cernohuby
+* @author Vladimir Stastka
+* @author Jakub Vrana
+*/
+
+$possible_drivers[] = "SQLSRV";
+$possible_drivers[] = "MSSQL";
+if (extension_loaded("sqlsrv") || extension_loaded("mssql")) {
+       $drivers["mssql"] = "MS SQL";
+}
+
+if (isset($_GET["mssql"])) {
+       define("DRIVER", "mssql");
+       if (extension_loaded("sqlsrv")) {
+               class Min_DB {
+                       var $extension = "sqlsrv", $_link, $_result, $server_info, $affected_rows, $error;
+
+                       function _get_error() {
+                               $this->error = "";
+                               foreach (sqlsrv_errors() as $error) {
+                                       $this->error .= "$error[message]\n";
+                               }
+                               $this->error = rtrim($this->error);
+                       }
+
+                       function connect($server, $username, $password) {
+                               $this->_link = @sqlsrv_connect($server, array("UID" => $username, "PWD" => $password));
+                               if ($this->_link) {
+                                       $info = sqlsrv_server_info($this->_link);
+                                       $this->server_info = $info['SQLServerVersion'];
+                               } else {
+                                       $this->_get_error();
+                               }
+                               return (bool) $this->_link;
+                       }
+
+                       function quote($string) {
+                               return "'" . str_replace("'", "''", $string) . "'";
+                       }
+
+                       function select_db($database) {
+                               return $this->query("USE $database");
+                       }
+
+
+                       function query($query, $unbuffered = false) {
+                               $result = sqlsrv_query($this->_link, $query); //! , array(), ($unbuffered ? array() : array("Scrollable" => "keyset"))
+                               if (!$result) {
+                                       $this->_get_error();
+                                       return false;
+                               }
+                               return $this->store_result($result);
+                       }
+
+                       function multi_query($query) {
+                               $this->_result = sqlsrv_query($this->_link, $query);
+                               if (!$this->_result) {
+                                       $this->_get_error();
+                                       return false;
+                               }
+                               return true;
+                       }
+
+                       function store_result($result = null) {
+                               if (!$result) {
+                                       $result = $this->_result;
+                               }
+                               if (sqlsrv_field_metadata($result)) {
+                                       return new Min_Result($result);
+                               }
+                               $this->affected_rows = sqlsrv_rows_affected($result);
+                               return true;
+                       }
+
+                       function next_result() {
+                               return sqlsrv_next_result($this->_result);
+                       }
+
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!is_object($result)) {
+                                       return false;
+                               }
+                               $row = $result->fetch_row();
+                               return $row[$field];
+                       }
+               }
+
+               class Min_Result {
+                       var $_result, $_offset = 0, $_fields, $num_rows;
+
+                       function Min_Result($result) {
+                               $this->_result = $result;
+                               $this->num_rows = sqlsrv_has_rows($result); //! sqlsrv_num_rows($result)
+                       }
+
+                       function _convert($row) {
+                               foreach ((array) $row as $key => $val) {
+                                       if (is_a($val, 'DateTime')) {
+                                               $row[$key] = $val->format("Y-m-d H:i:s");
+                                       }
+                                       //! stream
+                               }
+                               return $row;
+                       }
+                       
+                       function fetch_assoc() {
+                               return $this->_convert(sqlsrv_fetch_array($this->_result, SQLSRV_FETCH_ASSOC, SQLSRV_SCROLL_NEXT));
+                       }
+
+                       function fetch_row() {
+                               return $this->_convert(sqlsrv_fetch_array($this->_result, SQLSRV_FETCH_NUMERIC, SQLSRV_SCROLL_NEXT));
+                       }
+
+                       function fetch_field() {
+                               if (!$this->_fields) {
+                                       $this->_fields = sqlsrv_field_metadata($this->_result);
+                               }
+                               $field = $this->_fields[$this->_offset++];
+                               $return = new stdClass;
+                               $return->name = $field["Name"];
+                               $return->orgname = $field["Name"];
+                               $return->type = ($field["Type"] == 1 ? 254 : 0);
+                               return $return;
+                       }
+
+                       function __destruct() {
+                               sqlsrv_free_stmt($this->_result);
+                       }
+               }
+               
+       } elseif (extension_loaded("mssql")) {
+               class Min_DB {
+                       var $extension = "MSSQL", $_link, $_result, $server_info, $affected_rows, $error;
+
+                       function connect($server, $username, $password) {
+                               $this->_link = @mssql_connect($server, $username, $password);
+                               if ($this->_link) {
+                                       $result = $this->query("SELECT SERVERPROPERTY('ProductLevel'), SERVERPROPERTY('Edition')");
+                                       $row = $result->fetch_row();
+                                       $this->server_info = $this->result("sp_server_info 2", 2)." [$row[0]] $row[1]";
+                               } else {
+                                       $this->error = mssql_get_last_message();
+                               }
+                               return (bool) $this->_link;
+                       }
+
+                       function quote($string) {
+                               return "'" . str_replace("'", "''", $string) . "'";
+                       }
+
+                       function select_db($database) {
+                               return mssql_select_db($database);
+                       }
+
+                       function query($query, $unbuffered = false) {
+                               $result = mssql_query($query, $this->_link); //! $unbuffered
+                               if (!$result) {
+                                       $this->error = mssql_get_last_message();
+                                       return false;
+                               }
+                               if ($result === true) {
+                                       $this->affected_rows = mssql_rows_affected($this->_link);
+                                       return true;
+                               }
+                               return new Min_Result($result);
+                       }
+
+                       function multi_query($query) {
+                               return $this->_result = $this->query($query);
+                       }
+
+                       function store_result() {
+                               return $this->_result;
+                       }
+
+                       function next_result() {
+                               return mssql_next_result($this->_result);
+                       }
+
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!is_object($result)) {
+                                       return false;
+                               }
+                               return mssql_result($result->_result, 0, $field);
+                       }
+               }
+
+               class Min_Result {
+                       var $_result, $_offset = 0, $_fields, $num_rows;
+
+                       function Min_Result($result) {
+                               $this->_result = $result;
+                               $this->num_rows = mssql_num_rows($result);
+                       }
+
+                       function fetch_assoc() {
+                               return mssql_fetch_assoc($this->_result);
+                       }
+
+                       function fetch_row() {
+                               return mssql_fetch_row($this->_result);
+                       }
+
+                       function num_rows() {
+                               return mssql_num_rows($this->_result);
+                       }
+
+                       function fetch_field() {
+                               $return = mssql_fetch_field($this->_result);
+                               $return->orgtable = $return->table;
+                               $return->orgname = $return->name;
+                               return $return;
+                       }
+
+                       function __destruct() {
+                               mssql_free_result($this->_result);
+                       }
+               }
+               
+       }
+
+       function idf_escape($idf) {
+               return "[" . str_replace("]", "]]", $idf) . "]";
+       }
+
+       function connect() {
+               global $adminer;
+               $connection = new Min_DB;
+               $credentials = $adminer->credentials();
+               if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
+                       return $connection;
+               }
+               return $connection->error;
+       }
+
+       function get_databases() {
+               return get_vals("EXEC sp_databases");
+       }
+
+       function limit($query, $limit, $offset = 0) {
+               return (isset($limit) ? " TOP ($limit)" : "") . " $query"; //! offset
+       }
+
+       function limit1($query, $limit, $offset = 0) {
+               return limit($query, 1);
+       }
+
+       function db_collation($db, $collations) {
+               global $connection;
+               return $connection->result("SELECT collation_name FROM sys.databases WHERE name =  " . $connection->quote($db));
+       }
+
+       function engines() {
+               return array();
+       }
+
+       function logged_user() {
+               global $connection;
+               return $connection->result("SELECT SUSER_NAME()");
+       }
+
+       function tables_list() {
+               return get_key_vals("SELECT TABLE_NAME, TABLE_TYPE FROM information_schema.TABLES");
+       }
+
+       function count_tables($databases) {
+               global $connection;
+               $return = array();
+               foreach ($databases as $db) {
+                       $connection->select_db($db);
+                       $return[$db] = $connection->result("SELECT COUNT(*) FROM information_schema.TABLES");
+               }
+               return $return;
+       }
+       
+       function table_status($name = "") {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT TABLE_NAME AS Name, TABLE_TYPE AS Engine FROM information_schema.TABLES" . ($name != "" ? " WHERE TABLE_NAME = " . $connection->quote($name) : ""));
+               while ($row = $result->fetch_assoc()) {
+                       if ($name != "") {
+                               return $row;
+                       }
+                       $return[$row["Name"]] = $row;
+               }
+               return $return;
+       }
+
+       function fk_support($table_status) {
+               return true;
+       }
+
+       function fields($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT * FROM information_schema.COLUMNS WHERE TABLE_NAME = " . $connection->quote($table));
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["COLUMN_NAME"]] = array(
+                               "field" => $row["COLUMN_NAME"],
+                               "full_type" => $row["DATA_TYPE"],
+                               "type" => $row["DATA_TYPE"],
+                               "length" => $row["CHARACTER_MAXIMUM_LENGTH"], //! NUMERIC_, DATETIME_?
+                               "default" => $row["COLUMN_DEFAULT"],
+                               "null" => ($row["IS_NULLABLE"] == "YES"),
+                               "collation" => $row["COLLATION_NAME"],
+                               "privileges" => array("insert" => 1, "select" => 1, "update" => 1),
+                               //! primary - is_identity in sys.columns
+                       );
+               }
+               return $return;
+       }
+
+       function indexes($table, $connection2 = null) {
+               global $connection;
+               if (!is_object($connection2)) {
+                       $connection2 = $connection;
+               }
+               $return = array();
+               // sp_statistics doesn't return information about primary key
+               $result = $connection2->query("SELECT indexes.name, key_ordinal, is_unique, is_primary_key, columns.name AS column_name
+FROM sys.indexes
+INNER JOIN sys.index_columns ON indexes.object_id = index_columns.object_id AND indexes.index_id = index_columns.index_id
+INNER JOIN sys.columns ON index_columns.object_id = columns.object_id AND index_columns.column_id = columns.column_id
+WHERE OBJECT_NAME(indexes.object_id) = " . $connection2->quote($table));
+               if ($result) {
+                       while ($row = $result->fetch_assoc()) {
+                               $return[$row["name"]]["type"] = ($row["is_primary_key"] ? "PRIMARY" : ($row["is_unique"] ? "UNIQUE" : "INDEX"));
+                               $return[$row["name"]]["columns"][$row["key_ordinal"]] = $row["column_name"];
+                       }
+               }
+               return $return;
+       }
+
+       function collations() {
+               $return = array();
+               foreach (get_vals("SELECT name FROM fn_helpcollations()") as $collation) {
+                       $return[ereg_replace("_.*", "", $collation)][] = $collation;
+               }
+               return $return;
+       }
+
+       function information_schema($db) {
+               return false;
+       }
+
+       function error() {
+               global $connection;
+               return nl_br(h(ereg_replace("^(\\[[^]]*])+", "", $connection->error)));
+       }
+       
+       function exact_value($val) {
+               global $connection;
+               return $connection->quote($val);
+       }
+
+       function rename_database($name, $collation) {
+               if ($collation) {
+                       queries("ALTER DATABASE " . idf_escape(DB) . " COLLATE " . idf_escape($collation));
+               }
+               return queries("ALTER DATABASE " . idf_escape(DB) . " MODIFY NAME = " . idf_escape($name)); //! false negative "The database name 'test2' has been set."
+       }
+
+       function auto_increment() {
+               return " IDENTITY";
+       }
+       
+       function explain($connection, $query) {
+               $connection->query("SET SHOWPLAN_ALL ON");
+               $return = $connection->query($query);
+               $connection->query("SET SHOWPLAN_ALL OFF"); // connection is used also for indexes
+               return $return;
+       }
+       
+       function support($feature) {
+               return ereg('^(view|routine|trigger)$', $feature);
+       }
+       
+       $driver = "mssql";
+       $types = array();
+       $structured_types = array();
+       foreach (array(
+               lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "int" => 10, "bigint" => 20, "bit" => 1, "decimal" => 0, "real" => 12, "float" => 53, "smallmoney" => 10, "money" => 20),
+               lang('Date and time') => array("date" => 10, "smalldatetime" => 19, "datetime" => 19, "datetime2" => 19, "time" => 8, "datetimeoffset" => 10),
+               lang('Strings') => array("char" => 8000, "varchar" => 8000, "text" => 2147483647, "nchar" => 4000, "nvarchar" => 4000, "ntext" => 1073741823),
+               lang('Binary') => array("binary" => 8000, "varbinary" => 8000, "image" => 2147483647),
+       ) as $key => $val) {
+               $types += $val;
+               $structured_types[$key] = array_keys($val);
+       }
+       $unsigned = array();
+       $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL");
+       $functions = array("len", "lower", "round", "upper");
+       $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
+       $edit_functions = array(
+               array(
+                       "date|time" => "getdate",
+               ), array(
+                       "int|decimal|real|float|money|datetime" => "+/-",
+                       "char|text" => "+",
+               )
+       );
+}
diff --git a/adminer/drivers/mysql.inc.php b/adminer/drivers/mysql.inc.php
new file mode 100644 (file)
index 0000000..f600674
--- /dev/null
@@ -0,0 +1,713 @@
+<?php
+$possible_drivers[] = "MySQLi";
+$possible_drivers[] = "MySQL";
+$possible_drivers[] = "PDO_MySQL";
+if (extension_loaded("mysqli") || extension_loaded("mysql") || extension_loaded("pdo_mysql")) {
+       $drivers = array("server" => "MySQL") + $drivers;
+}
+
+if (!defined("DRIVER")) {
+       define("DRIVER", "server"); // server - backwards compatibility
+       // MySQLi supports everything, MySQL doesn't support multiple result sets, PDO_MySQL doesn't support orgtable
+       if (extension_loaded("mysqli")) {
+               class Min_DB extends MySQLi {
+                       var $extension = "MySQLi";
+                       
+                       function Min_DB() {
+                               parent::init();
+                       }
+                       
+                       function connect($server, $username, $password) {
+                               list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket
+                               return @$this->real_connect(
+                                       ($server != "" ? $host : ini_get("mysqli.default_host")),
+                                       ("$server$username" != "" ? $username : ini_get("mysqli.default_user")),
+                                       ("$server$username$password" != "" ? $password : ini_get("mysqli.default_pw")),
+                                       null,
+                                       (is_numeric($port) ? $port : ini_get("mysqli.default_port")),
+                                       (!is_numeric($port) ? $port : null)
+                               );
+                       }
+                       
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!$result) {
+                                       return false;
+                               }
+                               $row = $result->fetch_array();
+                               return $row[$field];
+                       }
+                       
+                       function quote($string) {
+                               return "'" . $this->escape_string($string) . "'";
+                       }
+               }
+               
+       } elseif (extension_loaded("mysql")) {
+               class Min_DB {
+                       var
+                               $extension = "MySQL", ///< @var string extension name
+                               $server_info, ///< @var string server version
+                               $affected_rows, ///< @var int number of affected rows
+                               $error, ///< @var string last error message
+                               $_link, $_result ///< @access private
+                       ;
+                       
+                       /** Connect to server
+                       * @param string
+                       * @param string
+                       * @param string
+                       * @return bool
+                       */
+                       function connect($server, $username, $password) {
+                               $this->_link = @mysql_connect(
+                                       ($server != "" ? $server : ini_get("mysql.default_host")),
+                                       ("$server$username" != "" ? $username : ini_get("mysql.default_user")),
+                                       ("$server$username$password" != "" ? $password : ini_get("mysql.default_password")),
+                                       true,
+                                       131072 // CLIENT_MULTI_RESULTS for CALL
+                               );
+                               if ($this->_link) {
+                                       $this->server_info = mysql_get_server_info($this->_link);
+                               } else {
+                                       $this->error = mysql_error();
+                               }
+                               return (bool) $this->_link;
+                       }
+                       
+                       /** Quote string to use in SQL
+                       * @param string
+                       * @return string escaped string enclosed in '
+                       */
+                       function quote($string) {
+                               return "'" . mysql_real_escape_string($string, $this->_link) . "'";
+                       }
+                       
+                       /** Select database
+                       * @param string
+                       * @return bool
+                       */
+                       function select_db($database) {
+                               return mysql_select_db($database, $this->_link);
+                       }
+                       
+                       /** Send query
+                       * @param string
+                       * @param bool
+                       * @return mixed bool or Min_Result
+                       */
+                       function query($query, $unbuffered = false) {
+                               $result = @($unbuffered ? mysql_unbuffered_query($query, $this->_link) : mysql_query($query, $this->_link)); // @ - mute mysql.trace_mode
+                               if (!$result) {
+                                       $this->error = mysql_error($this->_link);
+                                       return false;
+                               }
+                               if ($result === true) {
+                                       $this->affected_rows = mysql_affected_rows($this->_link);
+                                       $this->info = mysql_info($this->_link);
+                                       return true;
+                               }
+                               return new Min_Result($result);
+                       }
+                       
+                       /** Send query with more resultsets
+                       * @param string
+                       * @return bool
+                       */
+                       function multi_query($query) {
+                               return $this->_result = $this->query($query);
+                       }
+                       
+                       /** Get current resultset
+                       * @return Min_Result
+                       */
+                       function store_result() {
+                               return $this->_result;
+                       }
+                       
+                       /** Fetch next resultset
+                       * @return bool
+                       */
+                       function next_result() {
+                               // MySQL extension doesn't support multiple results
+                               return false;
+                       }
+                       
+                       /** Get single field from result
+                       * @param string
+                       * @param int
+                       * @return string
+                       */
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!$result) {
+                                       return false;
+                               }
+                               return mysql_result($result->_result, 0, $field);
+                       }
+               }
+               
+               class Min_Result {
+                       var
+                               $num_rows, ///< @var int number of rows in the result
+                               $_result ///< @access private
+                       ;
+                       
+                       /** Constructor
+                       * @param resource
+                       */
+                       function Min_Result($result) {
+                               $this->_result = $result;
+                               $this->num_rows = mysql_num_rows($result);
+                       }
+                       
+                       /** Fetch next row as associative array
+                       * @return array
+                       */
+                       function fetch_assoc() {
+                               return mysql_fetch_assoc($this->_result);
+                       }
+                       
+                       /** Fetch next row as numbered array
+                       * @return array
+                       */
+                       function fetch_row() {
+                               return mysql_fetch_row($this->_result);
+                       }
+                       
+                       /** Fetch next field
+                       * @return object properties: name, type, orgtable, orgname, charsetnr
+                       */
+                       function fetch_field() {
+                               $return = mysql_fetch_field($this->_result);
+                               $return->orgtable = $return->table;
+                               $return->orgname = $return->name;
+                               $return->charsetnr = ($return->blob ? 63 : 0);
+                               return $return;
+                       }
+                       
+                       /** Free result set
+                       */
+                       function __destruct() {
+                               mysql_free_result($this->_result); //! not called in PHP 4 which is a problem with mysql.trace_mode
+                       }
+               }
+               
+       } elseif (extension_loaded("pdo_mysql")) {
+               class Min_DB extends Min_PDO {
+                       var $extension = "PDO_MySQL";
+                       
+                       function connect($server, $username, $password) {
+                               $this->dsn("mysql:host=" . str_replace(":", ";unix_socket=", preg_replace('~:([0-9])~', ';port=\\1', $server)), $username, $password);
+                               return true;
+                       }
+                       
+                       function select_db($database) {
+                               // database selection is separated from the connection so dbname in DSN can't be used
+                               return $this->query("USE " . idf_escape($database));
+                       }
+                       
+                       function query($query, $unbuffered = false) {
+                               $this->setAttribute(1000, !$unbuffered); // 1000 - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
+                               return parent::query($query, $unbuffered);
+                       }
+               }
+               
+       }
+
+       /** Escape database identifier
+       * @param string
+       * @return string
+       */
+       function idf_escape($idf) {
+               return "`" . str_replace("`", "``", $idf) . "`";
+       }
+
+       /** Connect to the database
+       * @return mixed Min_DB or string for error
+       */
+       function connect() {
+               global $adminer;
+               $connection = new Min_DB;
+               $credentials = $adminer->credentials();
+               if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
+                       $connection->query("SET SQL_QUOTE_SHOW_CREATE=1");
+                       $connection->query("SET NAMES utf8");
+                       return $connection;
+               }
+               return $connection->error;
+       }
+
+       /** Get cached list of databases
+       * @param bool
+       * @return array
+       */
+       function get_databases($flush = true) {
+               // SHOW DATABASES can take a very long time so it is cached
+               $return = &get_session("databases");
+               if (!isset($return)) {
+                       if ($flush) {
+                               restart_session();
+                               ob_flush();
+                               flush();
+                       }
+                       $return = get_vals("SHOW DATABASES");
+               }
+               return $return;
+       }
+
+       /** Formulate SQL query with limit
+       * @param string everything after SELECT
+       * @param int
+       * @param int
+       * @return string
+       */
+       function limit($query, $limit, $offset = 0) {
+               return " $query" . (isset($limit) ? "\nLIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
+       }
+
+       /** Formulate SQL modification query with limit 1
+       * @param string everything after UPDATE or DELETE
+       * @return string
+       */
+       function limit1($query) {
+               return limit($query, 1);
+       }
+
+       /** Get database collation
+       * @param string
+       * @param array result of collations()
+       * @return string
+       */
+       function db_collation($db, $collations) {
+               global $connection;
+               $return = null;
+               $create = $connection->result("SHOW CREATE DATABASE " . idf_escape($db), 1);
+               if (preg_match('~ COLLATE ([^ ]+)~', $create, $match)) {
+                       $return = $match[1];
+               } elseif (preg_match('~ CHARACTER SET ([^ ]+)~', $create, $match)) {
+                       // default collation
+                       $return = $collations[$match[1]][0];
+               }
+               return $return;
+       }
+
+       /** Get supported engines
+       * @return array
+       */
+       function engines() {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SHOW ENGINES");
+               while ($row = $result->fetch_assoc()) {
+                       if (ereg("YES|DEFAULT", $row["Support"])) {
+                               $return[] = $row["Engine"];
+                       }
+               }
+               return $return;
+       }
+
+       /** Get logged user
+       * @return string
+       */
+       function logged_user() {
+               global $connection;
+               return $connection->result("SELECT USER()");
+       }
+
+       /** Get tables list
+       * @return array
+       */
+       function tables_list() {
+               global $connection;
+               return get_key_vals("SHOW" . ($connection->server_info >= 5 ? " FULL" : "") . " TABLES");
+       }
+
+       /** Count tables in all databases
+       * @param array
+       * @return array array($db => $tables)
+       */
+       function count_tables($databases) {
+               $return = array();
+               foreach ($databases as $db) {
+                       $return[$db] = count(get_vals("SHOW TABLES IN " . idf_escape($db)));
+               }
+               return $return;
+       }
+
+       /** Get table status
+       * @param string
+       * @return array
+       */
+       function table_status($name = "") {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SHOW TABLE STATUS" . ($name != "" ? " LIKE " . $connection->quote(addcslashes($name, "%_")) : ""));
+               while ($row = $result->fetch_assoc()) {
+                       if ($row["Engine"] == "InnoDB") {
+                               // ignore internal comment, unnecessary since MySQL 5.1.21
+                               $row["Comment"] = preg_replace('~(?:(.+); )?InnoDB free: .*~', '\\1', $row["Comment"]);
+                       }
+                       if (!isset($row["Rows"])) {
+                               $row["Engine"] = "VIEW";
+                               $row["Comment"] = "";
+                       }
+                       if ($name != "") {
+                               return $row;
+                       }
+                       $return[$row["Name"]] = $row;
+               }
+               return $return;
+       }
+
+       /** Check if table supports foreign keys
+       * @param array result of table_status
+       * @return bool
+       */
+       function fk_support($table_status) {
+               return ($table_status["Engine"] == "InnoDB");
+       }
+
+       /** Get information about fields
+       * @param string
+       * @return array array($name => array("field" => , "full_type" => , "type" => , "length" => , "unsigned" => , "default" => , "null" => , "auto_increment" => , "on_update" => , "collation" => , "privileges" => , "comment" => , "primary" => ))
+       */
+       function fields($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SHOW FULL COLUMNS FROM " . idf_escape($table));
+               if ($result) {
+                       while ($row = $result->fetch_assoc()) {
+                               preg_match('~^([^( ]+)(?:\\((.+)\\))?( unsigned)?( zerofill)?$~', $row["Type"], $match);
+                               $return[$row["Field"]] = array(
+                                       "field" => $row["Field"],
+                                       "full_type" => $row["Type"],
+                                       "type" => $match[1],
+                                       "length" => $match[2],
+                                       "unsigned" => ltrim($match[3] . $match[4]),
+                                       "default" => ($row["Default"] != "" || ereg("char", $match[1]) ? $row["Default"] : null),
+                                       "null" => ($row["Null"] == "YES"),
+                                       "auto_increment" => ($row["Extra"] == "auto_increment"),
+                                       "on_update" => (eregi('^on update (.+)', $row["Extra"], $match) ? $match[1] : ""), //! available since MySQL 5.1.23
+                                       "collation" => $row["Collation"],
+                                       "privileges" => array_flip(explode(",", $row["Privileges"])),
+                                       "comment" => $row["Comment"],
+                                       "primary" => ($row["Key"] == "PRI"),
+                               );
+                       }
+               }
+               return $return;
+       }
+
+       /** Get table indexes
+       * @param string
+       * @param string Min_DB to use
+       * @return array array($key_name => array("type" => , "columns" => array(), "lengths" => array()))
+       */
+       function indexes($table, $connection2 = null) {
+               global $connection;
+               if (!is_object($connection2)) { // use the main connection if the separate connection is unavailable
+                       $connection2 = $connection;
+               }
+               $return = array();
+               $result = $connection2->query("SHOW INDEX FROM " . idf_escape($table));
+               if ($result) {
+                       while ($row = $result->fetch_assoc()) {
+                               $return[$row["Key_name"]]["type"] = ($row["Key_name"] == "PRIMARY" ? "PRIMARY" : ($row["Index_type"] == "FULLTEXT" ? "FULLTEXT" : ($row["Non_unique"] ? "INDEX" : "UNIQUE")));
+                               $return[$row["Key_name"]]["columns"][] = $row["Column_name"];
+                               $return[$row["Key_name"]]["lengths"][] = $row["Sub_part"];
+                       }
+               }
+               return $return;
+       }
+
+       /** Get foreign keys in table
+       * @param string
+       * @return array array($name => array("db" => , "table" => , "source" => array(), "target" => array(), "on_delete" => , "on_update" => ))
+       */
+       function foreign_keys($table) {
+               global $connection, $on_actions;
+               static $pattern = '`(?:[^`]|``)+`';
+               $return = array();
+               $create_table = $connection->result("SHOW CREATE TABLE " . idf_escape($table), 1);
+               if ($create_table) {
+                       preg_match_all("~CONSTRAINT ($pattern) FOREIGN KEY \\(((?:$pattern,? ?)+)\\) REFERENCES ($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE (" . implode("|", $on_actions) . "))?(?: ON UPDATE (" . implode("|", $on_actions) . "))?~", $create_table, $matches, PREG_SET_ORDER);
+                       foreach ($matches as $match) {
+                               preg_match_all("~$pattern~", $match[2], $source);
+                               preg_match_all("~$pattern~", $match[5], $target);
+                               $return[idf_unescape($match[1])] = array(
+                                       "db" => idf_unescape($match[4] != "" ? $match[3] : $match[4]),
+                                       "table" => idf_unescape($match[4] != "" ? $match[4] : $match[3]),
+                                       "source" => array_map('idf_unescape', $source[0]),
+                                       "target" => array_map('idf_unescape', $target[0]),
+                                       "on_delete" => $match[6],
+                                       "on_update" => $match[7],
+                               );
+                       }
+               }
+               return $return;
+       }
+
+       /** Get view SELECT
+       * @param string
+       * @return array array("select" => )
+       */
+       function view($name) {
+               global $connection;
+               return array("select" => preg_replace('~^(?:[^`]|`[^`]*`)* AS ~U', '', $connection->result("SHOW CREATE VIEW " . idf_escape($name), 1)));
+       }
+
+       /** Get sorted grouped list of collations
+       * @return array
+       */
+       function collations() {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SHOW COLLATION");
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["Charset"]][] = $row["Collation"];
+               }
+               ksort($return);
+               foreach ($return as $key => $val) {
+                       sort($return[$key]);
+               }
+               return $return;
+       }
+
+       /** Find out if database is information_schema
+       * @param string
+       * @return bool
+       */
+       function information_schema($db) {
+               global $connection;
+               return ($connection->server_info >= 5 && $db == "information_schema");
+       }
+
+       /** Get escaped error message
+       * @return string
+       */
+       function error() {
+               global $connection;
+               return h(preg_replace('~^You have an error.*syntax to use~U', "Syntax error", $connection->error));
+       }
+
+       /** Return expression for binary comparison
+       * @param string
+       * @return string
+       */
+       function exact_value($val) {
+               global $connection;
+               return "BINARY " . $connection->quote($val);
+       }
+
+       /** Rename database from DB
+       * @param string new name
+       * @return string
+       * @return bool
+       */
+       function rename_database($name, $collation) {
+               global $connection;
+               $return = false;
+               if (queries("CREATE DATABASE " . idf_escape($name) . ($collation ? " COLLATE " . $connection->quote($collation) : ""))) {
+                       //! move triggers
+                       $return = true; // table list may by empty
+                       foreach (tables_list() as $table) {
+                               if (!queries("RENAME TABLE " . idf_escape($table) . " TO " . idf_escape($name) . "." . idf_escape($table))) {
+                                       $return = false;
+                                       break;
+                               }
+                       }
+                       if ($return) {
+                               queries("DROP DATABASE " . idf_escape(DB));
+                               //! saved to history of removed database
+                       }
+               }
+               return $return;
+       }
+       
+       /** Generate modifier for auto increment column
+       * @return string
+       */
+       function auto_increment() {
+               $auto_increment_index = " PRIMARY KEY";
+               // don't overwrite primary key by auto_increment
+               if ($_GET["create"] != "" && $_POST["auto_increment_col"]) {
+                       foreach (indexes($_GET["create"]) as $index) {
+                               if (in_array($_POST["fields"][$_POST["auto_increment_col"]]["orig"], $index["columns"], true)) {
+                                       $auto_increment_index = "";
+                                       break;
+                               }
+                               if ($index["type"] == "PRIMARY") {
+                                       $auto_increment_index = " UNIQUE";
+                               }
+                       }
+               }
+               return " AUTO_INCREMENT$auto_increment_index";
+       }
+       
+       /** Run commands to create or alter table
+       * @param string "" to create
+       * @param string new name
+       * @param array of array($orig, $process_field, $after)
+       * @param array of strings
+       * @param string
+       * @param string
+       * @param string
+       * @param int
+       * @param string
+       * @return bool
+       */
+       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
+               global $connection;
+               $alter = array();
+               foreach ($fields as $field) {
+                       $alter[] = ($field[1]
+                               ? ($table != "" ? ($field[0] != "" ? "CHANGE " . idf_escape($field[0]) : "ADD") : " ") . " " . implode("", $field[1]) . ($table != "" ? " $field[2]" : "")
+                               : "DROP " . idf_escape($field[0])
+                       );
+               }
+               $alter = array_merge($alter, $foreign);
+               $status = "COMMENT=" . $connection->quote($comment)
+                       . ($engine ? " ENGINE=" . $connection->quote($engine) : "")
+                       . ($collation ? " COLLATE " . $connection->quote($collation) : "")
+                       . ($auto_increment != "" ? " AUTO_INCREMENT=$auto_increment" : "")
+                       . $partitioning
+               ;
+               if ($table == "") {
+                       return queries("CREATE TABLE " . idf_escape($name) . " (\n" . implode(",\n", $alter) . "\n) $status");
+               }
+               if ($table != $name) {
+                       $alter[] = "RENAME TO " . idf_escape($name);
+               }
+               $alter[] = $status;
+               return queries("ALTER TABLE " . idf_escape($table) . "\n" . implode(",\n", $alter));
+       }
+       
+       /** Run commands to alter indexes
+       * @param string escaped table name
+       * @param array of array("index type", "(columns definition)") or array("index type", "escaped name", "DROP")
+       * @return bool
+       */
+       function alter_indexes($table, $alter) {
+               foreach ($alter as $key => $val) {
+                       $alter[$key] = ($val[2] ? "\nDROP INDEX " : "\nADD $val[0] " . ($val[0] == "PRIMARY" ? "KEY " : "")) . $val[1];
+               }
+               return queries("ALTER TABLE " . idf_escape($table) . implode(",", $alter));
+       }
+       
+       /** Run commands to truncate tables
+       * @param array
+       * @return bool
+       */
+       function truncate_tables($tables) {
+               foreach ($tables as $table) {
+                       if (!queries("TRUNCATE TABLE " . idf_escape($table))) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       /** Drop views
+       * @param array
+       * @return bool
+       */
+       function drop_views($views) {
+               return queries("DROP VIEW " . implode(", ", array_map('idf_escape', $views)));
+       }
+       
+       /** Drop tables
+       * @param array
+       * @return bool
+       */
+       function drop_tables($tables) {
+               return queries("DROP TABLE " . implode(", ", array_map('idf_escape', $tables)));
+       }
+       
+       /** Get information about trigger
+       * @param string trigger name
+       * @return array array("Trigger" => , "Timing" => , "Event" => , "Statement" => )
+       */
+       function trigger($name) {
+               global $connection;
+               $result = $connection->query("SHOW TRIGGERS WHERE `Trigger` = " . $connection->quote($name));
+               return $result->fetch_assoc();
+       }
+       
+       /** Get defined triggers
+       * @param string
+       * @return array array($name => array($timing, $event))
+       */
+       function triggers($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SHOW TRIGGERS LIKE " . $connection->quote(addcslashes($table, "%_")));
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["Trigger"]] = array($row["Timing"], $row["Event"]);
+               }
+               return $return;
+       }
+       
+       /** Explain select
+       * @param Min_DB
+       * @param string
+       * @return Min_Result
+       */
+       function explain($connection, $query) {
+               return $connection->query("EXPLAIN $query");
+       }
+       
+       /** Get SQL command to create table
+       * @param string
+       * @return string
+       */
+       function create_sql($table) {
+               global $connection;
+               return $connection->result("SHOW CREATE TABLE " . idf_escape($table), 1);
+       }
+       
+       /** Check whether a feature is supported
+       * @param string
+       * @return bool
+       */
+       function support($feature) {
+               global $connection;
+               $features = array(
+                       "view" => ($connection->server_info >= 5),
+                       "routine" => ($connection->server_info >= 5),
+                       "trigger" => ($connection->server_info >= 5),
+                       "event" => ($connection->server_info >= 5.1),
+                       "partitioning" => ($connection->server_info >= 5.1),
+               );
+               return (isset($features[$feature]) ? $features[$feature] : true);
+       }
+
+       $driver = "sql"; ///< @var string JUSH identifier
+       $types = array(); ///< @var array ($type => $maximum_unsigned_length, ...)
+       $structured_types = array(); ///< @var array ($description => array($type, ...), ...)
+       foreach (array(
+               lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "mediumint" => 8, "int" => 10, "bigint" => 20, "decimal" => 66, "float" => 12, "double" => 21),
+               lang('Date and time') => array("date" => 10, "datetime" => 19, "timestamp" => 19, "time" => 10, "year" => 4),
+               lang('Strings') => array("char" => 255, "varchar" => 65535, "tinytext" => 255, "text" => 65535, "mediumtext" => 16777215, "longtext" => 4294967295),
+               lang('Binary') => array("binary" => 255, "varbinary" => 65535, "tinyblob" => 255, "blob" => 65535, "mediumblob" => 16777215, "longblob" => 4294967295),
+               lang('Lists') => array("enum" => 65535, "set" => 64),
+       ) as $key => $val) {
+               $types += $val;
+               $structured_types[$key] = array_keys($val);
+       }
+       $unsigned = array("unsigned", "zerofill", "unsigned zerofill"); ///< @var array number variants
+       $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "REGEXP", "IN", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL"); ///< @var array operators used in select
+       $functions = array("char_length", "from_unixtime", "hex", "lower", "round", "sec_to_time", "time_to_sec", "upper"); ///< @var array functions used in select
+       $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"); ///< @var array grouping functions used in select
+       $edit_functions = array( ///< @var array of array("$type|$type2" => "$function/$function2") functions used in editing, [0] - edit and insert, [1] - edit only
+               array(
+                       "char" => "md5/sha1/password/encrypt/uuid", //! JavaScript for disabling maxlength
+                       "date|time" => "now",
+               ), array(
+                       "int|float|double|decimal" => "+/-",
+                       "date" => "+ interval/- interval",
+                       "time" => "addtime/subtime",
+                       "char|text" => "concat",
+               )
+       );
+}
diff --git a/adminer/drivers/pgsql.inc.php b/adminer/drivers/pgsql.inc.php
new file mode 100644 (file)
index 0000000..a42a21e
--- /dev/null
@@ -0,0 +1,443 @@
+<?php
+$possible_drivers[] = "PgSQL";
+$possible_drivers[] = "PDO_PgSQL";
+if (extension_loaded("pgsql") || extension_loaded("pdo_pgsql")) {
+       $drivers["pgsql"] = "PostgreSQL";
+}
+
+if (isset($_GET["pgsql"])) {
+       define("DRIVER", "pgsql");
+       if (extension_loaded("pgsql")) {
+               class Min_DB {
+                       var $extension = "PgSQL", $_link, $_result, $_string, $_database = true, $server_info, $affected_rows, $error;
+                       
+                       function _error($errno, $error) {
+                               if (ini_bool("html_errors")) {
+                                       $error = html_entity_decode(strip_tags($error));
+                               }
+                               $error = ereg_replace('^[^:]*: ', '', $error);
+                               $this->error = $error;
+                       }
+                       
+                       function connect($server, $username, $password) {
+                               set_error_handler(array($this, '_error'));
+                               $this->_string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
+                               $this->_link = @pg_connect($this->_string . (DB != "" ? " dbname='" . addcslashes(DB, "'\\") . "'" : ""), PGSQL_CONNECT_FORCE_NEW);
+                               if (!$this->_link && DB != "") {
+                                       // try to connect directly with database for performance
+                                       $this->_database = false;
+                                       $this->_link = @pg_connect($this->_string, PGSQL_CONNECT_FORCE_NEW);
+                               }
+                               restore_error_handler();
+                               if ($this->_link) {
+                                       $version = pg_version($this->_link);
+                                       $this->server_info = $version["server"];
+                                       pg_set_client_encoding($this->_link, "UTF8");
+                               }
+                               return (bool) $this->_link;
+                       }
+                       
+                       function quote($string) {
+                               return "'" . pg_escape_string($this->_link, $string) . "'"; //! bytea
+                       }
+                       
+                       function select_db($database) {
+                               if ($database == DB) {
+                                       return $this->_database;
+                               }
+                               $link = @pg_connect($this->_connection . " dbname='" . addcslashes($database, "'\\") . "'", PGSQL_CONNECT_FORCE_NEW);
+                               if ($link) {
+                                       $this->_link = $link;
+                               }
+                               return $link;
+                       }
+                       
+                       function query($query, $unbuffered = false) {
+                               $result = @pg_query($this->_link, $query);
+                               if (!$result) {
+                                       $this->error = pg_last_error($this->_link);
+                                       return false;
+                               } elseif (!pg_num_fields($result)) {
+                                       $this->affected_rows = pg_affected_rows($result);
+                                       return true;
+                               }
+                               return new Min_Result($result);
+                       }
+                       
+                       function multi_query($query) {
+                               return $this->_result = $this->query($query);
+                       }
+                       
+                       function store_result() {
+                               return $this->_result;
+                       }
+                       
+                       function next_result() {
+                               // PgSQL extension doesn't support multiple results
+                               return false;
+                       }
+                       
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!$result) {
+                                       return false;
+                               }
+                               return pg_fetch_result($result->_result, 0, $field);
+                       }
+               }
+               
+               class Min_Result {
+                       var $_result, $_offset = 0, $num_rows;
+                       
+                       function Min_Result($result) {
+                               $this->_result = $result;
+                               $this->num_rows = pg_num_rows($result);
+                       }
+                       
+                       function fetch_assoc() {
+                               return pg_fetch_assoc($this->_result);
+                       }
+                       
+                       function fetch_row() {
+                               return pg_fetch_row($this->_result);
+                       }
+                       
+                       function fetch_field() {
+                               $column = $this->_offset++;
+                               $row = new stdClass;
+                               $row->orgtable = pg_field_table($this->_result, $column);
+                               $row->name = pg_field_name($this->_result, $column);
+                               $row->orgname = $row->name;
+                               $row->type = pg_field_type($this->_result, $column);
+                               $row->charsetnr = ($row->type == "bytea" ? 63 : 0);
+                               return $row;
+                       }
+                       
+                       function __destruct() {
+                               pg_free_result($this->_result);
+                       }
+               }
+               
+       } elseif (extension_loaded("pdo_pgsql")) {
+               class Min_DB extends Min_PDO {
+                       var $extension = "PDO_PgSQL";
+                       
+                       function connect($server, $username, $password) {
+                               $string = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' options='-c client_encoding=utf8'";
+                               $this->dsn($string . (DB != "" ? " dbname='" . addcslashes(DB, "'\\") . "'" : ""), $username, $password);
+                               //! connect without DB in case of an error
+                               return true;
+                       }
+                       
+                       function select_db($database) {
+                               return (DB == $database);
+                       }
+               }
+               
+       }
+       
+       function idf_escape($idf) {
+               return '"' . str_replace('"', '""', $idf) . '"';
+       }
+
+       function connect() {
+               global $adminer;
+               $connection = new Min_DB;
+               $credentials = $adminer->credentials();
+               if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
+                       return $connection;
+               }
+               return $connection->error;
+       }
+       
+       function get_databases() {
+               return get_vals("SELECT datname FROM pg_database");
+       }
+       
+       function limit($query, $limit, $offset = 0) {
+               return " $query" . (isset($limit) ? "\nLIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
+       }
+
+       function limit1($query) {
+               return " $query";
+       }
+       
+       function db_collation($db, $collations) {
+               global $connection;
+               return $connection->result("SHOW LC_COLLATE"); //! respect $db
+       }
+
+       function engines() {
+               return array();
+       }
+       
+       function logged_user() {
+               global $connection;
+               return $connection->result("SELECT user");
+       }
+       
+       function tables_list() {
+               global $connection;
+               return get_key_vals("SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name");
+       }
+       
+       function count_tables($databases) {
+               return array(); // would require reconnect
+       }
+
+       function table_status($name = "") {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT relname AS \"Name\", CASE relkind WHEN 'r' THEN '' ELSE 'view' END AS \"Engine\", pg_relation_size(oid) AS \"Data_length\", pg_catalog.obj_description(oid, 'pg_class') AS \"Comment\" FROM pg_catalog.pg_class WHERE relkind IN ('r','v') AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')" . ($name != "" ? " AND relname = " . $connection->quote($name) : "")); //! Index_length, Auto_increment
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["Name"]] = $row;
+               }
+               return ($name != "" ? $return[$name] : $return);
+       }
+       
+       function fk_support($table_status) {
+               return true;
+       }
+       
+       function fields($table) {
+               global $connection;
+               $return = array();
+               $table_oid = $connection->result("SELECT oid FROM pg_class WHERE relname = " . $connection->quote($table));
+               $result = $connection->query("SELECT *, col_description($table_oid, ordinal_position) AS comment FROM information_schema.columns WHERE table_name = " . $connection->quote($table) . " ORDER BY ordinal_position");
+               if ($result) {
+                       while ($row = $result->fetch_assoc()) {
+                               $length = $row["character_maximum_length"];
+                               $return[$row["column_name"]] = array(
+                                       "field" => $row["column_name"],
+                                       "full_type" => $row["data_type"] . ($length ? "($length)" : ""),
+                                       "type" => $row["data_type"],
+                                       "length" => $length,
+                                       "default" => $row["column_default"],
+                                       "null" => ($row["is_nullable"] == "YES"),
+                                       "auto_increment" => eregi("^nextval\\(", $row["column_default"]),
+                                       "on_update" => "", //!
+                                       "collation" => $row["collation_name"],
+                                       "privileges" => array("insert" => 1, "select" => 1, "update" => 1), //! is_updatable
+                                       "primary" => false, //!
+                                       "comment" => $row["comment"],
+                               );
+                       }
+               }
+               return $return;
+       }
+       
+       function indexes($table, $connection2 = null) {
+               global $connection;
+               if (!is_object($connection2)) {
+                       $connection2 = $connection;
+               }
+               $return = array();
+               $table_oid = $connection2->result("SELECT oid FROM pg_class WHERE relname = " . $connection2->quote($table));
+               $columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
+               $result = $connection2->query("SELECT relname, indisunique, indisprimary, indkey FROM pg_index i, pg_class ci WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid");
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["relname"]]["type"] = ($row["indisprimary"] == "t" ? "PRIMARY" : ($row["indisunique"] == "t" ? "UNIQUE" : "INDEX"));
+                       $return[$row["relname"]]["columns"] = array();
+                       foreach (explode(" ", $row["indkey"]) as $indkey) {
+                               $return[$row["relname"]]["columns"][] = $columns[$indkey];
+                       }
+                       $return[$row["relname"]]["lengths"] = array();
+               }
+               return $return;
+       }
+       
+       function foreign_keys($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT tc.constraint_name, kcu.column_name, rc.update_rule AS on_update, rc.delete_rule AS on_delete, ccu.table_name AS table, ccu.column_name AS ref
+FROM information_schema.table_constraints tc
+LEFT JOIN information_schema.key_column_usage kcu USING (constraint_catalog, constraint_schema, constraint_name)
+LEFT JOIN information_schema.referential_constraints rc USING (constraint_catalog, constraint_schema, constraint_name)
+LEFT JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_catalog = ccu.constraint_catalog AND rc.unique_constraint_schema = ccu.constraint_schema AND rc.unique_constraint_name = ccu.constraint_name
+WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = " . $connection->quote($table)); //! there can be more unique_constraint_name
+               while ($row = $result->fetch_assoc()) {
+                       $foreign_key = &$return[$row["constraint_name"]];
+                       if (!$foreign_key) {
+                               $foreign_key = $row;
+                       }
+                       $foreign_key["source"][] = $row["column_name"];
+                       $foreign_key["target"][] = $row["ref"];
+               }
+               return $return;
+       }
+       
+       function view($name) {
+               global $connection;
+               return array("select" => $connection->result("SELECT pg_get_viewdef(" . $connection->quote($name) . ")"));
+       }
+       
+       function collations() {
+               //! supported in CREATE DATABASE
+               return array();
+       }
+       
+       function information_schema($db) {
+               return ($db == "information_schema");
+       }
+       
+       function error() {
+               global $connection;
+               $return = h($connection->error);
+               if (preg_match('~^(.*\\n)?([^\\n]*)\\n( *)\\^(\\n.*)?$~s', $return, $match)) {
+                       $return = $match[1] . preg_replace('~((?:[^&]|&[^;]*;){' . strlen($match[3]) . '})(.*)~', '\\1<b>\\2</b>', $match[2]) . $match[4];
+               }
+               return nl_br($return);
+       }
+       
+       function exact_value($val) {
+               global $connection;
+               return $connection->quote($val);
+       }
+       
+       function rename_database($name, $collation) {
+               //! current database cannot be renamed
+               return queries("ALTER DATABASE " . idf_escape(DB) . " RENAME TO " . idf_escape($name));
+       }
+       
+       function auto_increment() {
+               return true;
+       }
+       
+       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
+               global $connection;
+               $alter = array();
+               $queries = array();
+               foreach ($fields as $field) {
+                       $column = idf_escape($field[0]);
+                       $val = $field[1];
+                       if (!$val) {
+                               $alter[] = "DROP $column";
+                       } else {
+                               $val5 = $val[5];
+                               unset($val[5]);
+                               if ($val[6]) { // auto_increment
+                                       $val = array($val[0], ($val[1] == "bigint" ? "big" : "") . "serial");
+                               }
+                               if ($field[0] == "") {
+                                       $alter[] = ($table != "" ? "ADD " : "  ") . implode("", $val);
+                               } else {
+                                       if ($column != $val[0]) {
+                                               $queries[] = "ALTER TABLE " . idf_escape($table) . " RENAME $column TO $val[0]";
+                                       }
+                                       $alter[] = "ALTER $column TYPE $val[1]";
+                                       if (!$val[6]) {
+                                               $alter[] = "ALTER $column" . ($val[3] ? " SET$val[3]" : " DROP DEFAULT"); //! quoting
+                                               $alter[] = "ALTER $column " . ($val[2] == " NULL" ? "DROP NOT" : "SET") . $val[2];
+                                       }
+                               }
+                               if ($table != "" || $val5 != "") {
+                                       $queries[] = "COMMENT ON COLUMN " . idf_escape($table) . ".$val[0] IS " . substr($val5, 9);
+                               }
+                       }
+               }
+               $alter = array_merge($alter, $foreign);
+               if ($table == "") {
+                       array_unshift($queries, "CREATE TABLE " . idf_escape($name) . " (\n" . implode(",\n", $alter) . "\n)");
+               } elseif ($alter) {
+                       array_unshift($queries, "ALTER TABLE " . idf_escape($table) . "\n" . implode(",\n", $alter));
+               }
+               if ($table != "" && $table != $name) {
+                       $queries[] = "ALTER TABLE " . idf_escape($table) . " RENAME TO " . idf_escape($name);
+               }
+               if ($table != "" || $comment != "") {
+                       $queries[] = "COMMENT ON TABLE " . idf_escape($name) . " IS " . $connection->quote($comment);
+               }
+               if ($auto_increment != "") {
+                       //! $queries[] = "SELECT setval(pg_get_serial_sequence(" . $connection->quote($name) . ", ), $auto_increment)";
+               }
+               foreach ($queries as $query) {
+                       if (!queries($query)) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       function alter_indexes($table, $alter) {
+               $create = array();
+               $drop = array();
+               foreach ($alter as $val) {
+                       if ($val[0] != "INDEX") {
+                               $create[] = ($val[2] ? "\nDROP CONSTRAINT " : "\nADD $val[0] " . ($val[0] == "PRIMARY" ? "KEY " : "")) . $val[1];
+                       } elseif ($val[2]) {
+                               $drop[] = $val[1];
+                       } elseif (!queries("CREATE INDEX " . idf_escape(uniqid($table . "_")) . " ON " . idf_escape($table) . " $val[1]")) {
+                               return false;
+                       }
+               }
+               return ((!$create || queries("ALTER TABLE " . idf_escape($table) . implode(",", $create)))
+                       && (!$drop || queries("DROP INDEX " . implode(", ", $drop)))
+               );
+       }
+       
+       function truncate_tables($tables) {
+               return queries("TRUNCATE " . implode(", ", array_map('idf_escape', $tables)));
+               return true;
+       }
+       
+       function drop_views($views) {
+               return queries("DROP VIEW " . implode(", ", array_map('idf_escape', $views)));
+       }
+       
+       function drop_tables($tables) {
+               return queries("DROP TABLE " . implode(", ", array_map('idf_escape', $tables)));
+       }
+       
+       function trigger($name) {
+               global $connection;
+               $result = $connection->query('SELECT trigger_name AS "Trigger", condition_timing AS "Timing", event_manipulation AS "Event", action_statement AS "Statement" FROM information_schema.triggers WHERE event_object_table = ' . $connection->quote($_GET["trigger"]) . ' AND trigger_name = ' . $connection->quote($name));
+               return $result->fetch_assoc();
+       }
+       
+       function triggers($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT * FROM information_schema.triggers WHERE event_object_table = " . $connection->quote($table));
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["trigger_name"]] = array($row["condition_timing"], $row["event_manipulation"]);
+               }
+               return $return;
+       }
+       
+       function explain($connection, $query) {
+               return $connection->query("EXPLAIN $query");
+       }
+       
+       function support($feature) {
+               return ereg('^(comment|view|routine|trigger)$', $feature);
+       }
+       
+       $driver = "pgsql";
+       $types = array();
+       $structured_types = array();
+       foreach (array( //! arrays
+               lang('Numbers') => array("smallint" => 5, "integer" => 10, "bigint" => 19, "boolean" => 1, "numeric" => 0, "real" => 7, "double precision" => 16, "money" => 20),
+               lang('Date and time') => array("date" => 13, "time" => 17, "timestamp" => 20, "interval" => 0),
+               lang('Strings') => array("character" => 0, "character varying" => 0, "text" => 0, "tsquery" => 0, "tsvector" => 0, "uuid" => 0, "xml" => 0),
+               lang('Binary') => array("bit" => 0, "bit varying" => 0, "bytea" => 0),
+               lang('Network') => array("cidr" => 43, "inet" => 43, "macaddr" => 17, "txid_snapshot" => 0),
+               lang('Geometry') => array("box" => 0, "circle" => 0, "line" => 0, "lseg" => 0, "path" => 0, "point" => 0, "polygon" => 0),
+       ) as $key => $val) {
+               $types += $val;
+               $structured_types[$key] = array_keys($val);
+       }
+       $unsigned = array();
+       $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL");
+       $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
+       $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
+       $edit_functions = array(
+               array(
+                       "char" => "md5",
+                       "date|time" => "now",
+               ), array(
+                       "int|numeric|real|money" => "+/-",
+                       "date|time" => "+ interval/- interval", //! escape
+                       "char|text" => "||",
+               )
+       );
+}
diff --git a/adminer/drivers/sqlite.inc.php b/adminer/drivers/sqlite.inc.php
new file mode 100644 (file)
index 0000000..48ee046
--- /dev/null
@@ -0,0 +1,479 @@
+<?php
+$possible_drivers[] = "SQLite";
+$possible_drivers[] = "SQLite3";
+$possible_drivers[] = "PDO_SQLite";
+if (extension_loaded("sqlite3") || extension_loaded("pdo_sqlite")) {
+       $drivers["sqlite"] = "SQLite 3";
+}
+if (extension_loaded("sqlite") || extension_loaded("pdo_sqlite")) {
+       $drivers["sqlite2"] = "SQLite 2";
+}
+
+if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
+       define("DRIVER", (isset($_GET["sqlite"]) ? "sqlite" : "sqlite2"));
+       if (extension_loaded(isset($_GET["sqlite2"]) ? "sqlite" : "sqlite3")) {
+               if (isset($_GET["sqlite2"])) {
+                       
+                       class Min_SQLite {
+                               var $extension = "SQLite", $server_info, $affected_rows, $error, $_connection;
+                               
+                               function __construct() {
+                                       $this->server_info = sqlite_libversion();
+                               }
+                               
+                               function open($filename) {
+                                       $this->_connection = new SQLiteDatabase($filename);
+                               }
+                               
+                               function query($query, $unbuffered = false) {
+                                       $method = ($unbuffered ? "unbufferedQuery" : "query");
+                                       $result = @$this->_connection->$method($query, SQLITE_BOTH, $error);
+                                       if (!$result) {
+                                               $this->error = $error;
+                                               return false;
+                                       } elseif ($result === true) {
+                                               $this->affected_rows = $this->changes();
+                                               return true;
+                                       }
+                                       return new Min_Result($result);
+                               }
+                               
+                               function quote($string) {
+                                       return "'" . sqlite_escape_string($string) . "'";
+                               }
+                               
+                               function result($query, $field = 0) {
+                                       $result = $this->query($query);
+                                       if (!$result) {
+                                               return false;
+                                       }
+                                       $row = $result->_result->fetch();
+                                       return $row[$field];
+                               }
+                       }
+                       
+                       class Min_Result {
+                               var $_result, $_offset = 0, $num_rows;
+                               
+                               function __construct($result) {
+                                       $this->_result = $result;
+                                       if (method_exists($result, 'numRows')) { // not available in unbuffered query
+                                               $this->num_rows = $result->numRows();
+                                       }
+                               }
+                               
+                               function fetch_assoc() {
+                                       $row = $this->_result->fetch(SQLITE_ASSOC);
+                                       if (!$row) {
+                                               return false;
+                                       }
+                                       $return = array();
+                                       foreach ($row as $key => $val) {
+                                               $return[($key[0] == '"' ? idf_unescape($key) : $key)] = $val;
+                                       }
+                                       return $return;
+                               }
+                               
+                               function fetch_row() {
+                                       return $this->_result->fetch(SQLITE_NUM);
+                               }
+                               
+                               function fetch_field() {
+                                       return (object) array(
+                                               "name" => $this->_result->fieldName($this->_offset++),
+                                               //! type, orgtable, charsetnr
+                                       );
+                               }
+                               
+                       }
+                       
+               } else {
+                       
+                       class Min_SQLite extends SQLite3 {
+                               var $extension = "SQLite3", $server_info, $affected_rows, $error;
+                               
+                               function __construct() {
+                                       $version = $this->version();
+                                       $this->server_info = $version["versionString"];
+                               }
+                               
+                               function open($filename) {
+                                       parent::__construct($filename);
+                               }
+                               
+                               function query($query) {
+                                       $result = @parent::query($query);
+                                       if (!$result) {
+                                               $this->error = $this->lastErrorMsg();
+                                               return false;
+                                       } elseif ($result->numColumns()) {
+                                               return new Min_Result($result);
+                                       }
+                                       $this->affected_rows = $this->changes();
+                                       return true;
+                               }
+                               
+                               function quote($string) {
+                                       return "'" . $this->escapeString($string) . "'";
+                               }
+                               
+                               function result($query, $field = 0) {
+                                       $result = $this->query($query);
+                                       if (!$result) {
+                                               return false;
+                                       }
+                                       $row = $result->_result->fetchArray();
+                                       return $row[$field];
+                               }
+                       }
+                       
+                       class Min_Result {
+                               var $_result, $_offset = 0, $num_rows;
+                               
+                               function __construct($result) {
+                                       $this->_result = $result;
+                                       $this->num_rows = 1; //!
+                               }
+                               
+                               function fetch_assoc() {
+                                       return $this->_result->fetchArray(SQLITE3_ASSOC);
+                               }
+                               
+                               function fetch_row() {
+                                       return $this->_result->fetchArray(SQLITE3_NUM);
+                               }
+                               
+                               function fetch_field() {
+                                       $column = $this->_offset++;
+                                       return (object) array(
+                                               "name" => $this->_result->columnName($column),
+                                               "type" => $this->_result->columnType($column),
+                                               //! orgtable, charsetnr
+                                       );
+                               }
+                               
+                               function __desctruct() {
+                                       return $this->_result->finalize();
+                               }
+                       }
+                       
+               }
+               
+               class Min_DB extends Min_SQLite {
+                       
+                       function select_db($filename) {
+                               static $connected = false;
+                               if ($connected) {
+                                       return true;
+                               }
+                               set_exception_handler('connect_error'); // try/catch is not compatible with PHP 4
+                               $this->open($filename);
+                               $connected = true;
+                               restore_exception_handler();
+                               return true;
+                       }
+                       
+                       function multi_query($query) {
+                               return $this->_result = $this->query($query);
+                       }
+                       
+                       function store_result() {
+                               return $this->_result;
+                       }
+                       
+                       function next_result() {
+                               return false;
+                       }
+               }
+               
+       } elseif (extension_loaded("pdo_sqlite")) {
+               class Min_DB extends Min_PDO {
+                       var $extension = "PDO_SQLite";
+                       
+                       function select_db($filename) {
+                               static $connected = false;
+                               if ($connected) {
+                                       return true;
+                               }
+                               $connected = true;
+                               $this->dsn(DRIVER . ":$filename", "", "", "connect_error");
+                               //! $this->server_info needs to be filled in __construct()
+                               return true;
+                       }
+               }
+               
+       }
+
+       function idf_escape($idf) {
+               return '"' . str_replace('"', '""', $idf) . '"';
+       }
+
+       function connect() {
+               global $connection;
+               if ($connection) {
+                       return $connection; // can connect only once, function to get number of rows doesn't exist anyway
+               }
+               return new Min_DB;
+       }
+
+       function get_databases() {
+               return array();
+       }
+
+       function limit($query, $limit, $offset = 0) {
+               return " $query" . (isset($limit) ? "\nLIMIT $limit" . ($offset ? " OFFSET $offset" : "") : "");
+       }
+
+       function limit1($query) {
+               global $connection;
+               return ($connection->result("SELECT sqlite_compileoption_used('ENABLE_UPDATE_DELETE_LIMIT')") ? limit($query, 1) : " $query");
+       }
+
+       function db_collation($db, $collations) {
+               return null;
+       }
+
+       function engines() {
+               return array();
+       }
+
+       function logged_user() {
+               return ""; //! OS user
+       }
+
+       function tables_list() {
+               return get_key_vals("SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view')", 1);
+       }
+
+       function count_tables($databases) {
+               return array();
+       }
+
+       function table_status($name = "") {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT name AS Name, type AS Engine FROM sqlite_master WHERE type IN ('table', 'view')" . ($name != "" ? " AND name = " . $connection->quote($name) : ""));
+               while ($row = $result->fetch_assoc()) {
+                       $return[$row["Name"]] = $row;
+               }
+               $result = $connection->query("SELECT * FROM sqlite_sequence");
+               if ($result) {
+                       while ($row = $result->fetch_assoc()) {
+                               $return[$row["name"]]["Auto_increment"] = $row["seq"];
+                       }
+               }
+               return ($name != "" ? $return[$name] : $return);
+       }
+
+       function fk_support($table_status) {
+               global $connection;
+               return !$connection->result("SELECT sqlite_compileoption_used('OMIT_FOREIGN_KEY')");
+       }
+
+       function fields($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("PRAGMA table_info(" . idf_escape($table) . ")");
+               if (is_object($result)) {
+                       while ($row = $result->fetch_assoc()) {
+                               $type = strtolower($row["type"]);
+                               $return[$row["name"]] = array(
+                                       "field" => $row["name"],
+                                       "type" => (eregi("int", $type) ? "integer" : (eregi("char|clob|text", $type) ? "text" : (eregi("blob", $type) ? "blob" : (eregi("real|floa|doub", $type) ? "real" : "numeric")))),
+                                       "full_type" => $type,
+                                       "default" => $row["dflt_value"],
+                                       "null" => !$row["notnull"],
+                                       "auto_increment" => eregi('^integer$', $type) && $row["pk"], //! possible false positive
+                                       "collation" => null, //!
+                                       "privileges" => array("select" => 1, "insert" => 1, "update" => 1),
+                                       "primary" => $row["pk"],
+                               );
+                       }
+               }
+               return $return;
+       }
+
+       function indexes($table, $connection2 = null) {
+               global $connection;
+               $return = array();
+               $primary = array();
+               foreach (fields($table) as $field) {
+                       if ($field["primary"]) {
+                               $primary[] = $field["field"];
+                       }
+               }
+               if ($primary) {
+                       $return[""] = array("type" => "PRIMARY", "columns" => $primary, "lengths" => array());
+               }
+               $result = $connection->query("PRAGMA index_list(" . idf_escape($table) . ")");
+               if (is_object($result)) {
+                       while ($row = $result->fetch_assoc()) {
+                               $return[$row["name"]]["type"] = ($row["unique"] ? "UNIQUE" : "INDEX");
+                               $return[$row["name"]]["lengths"] = array();
+                               $result1 = $connection->query("PRAGMA index_info(" . idf_escape($row["name"]) . ")");
+                               while ($row1 = $result1->fetch_assoc()) {
+                                       $return[$row["name"]]["columns"][] = $row1["name"];
+                               }
+                       }
+               }
+               return $return;
+       }
+
+       function foreign_keys($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("PRAGMA foreign_key_list(" . idf_escape($table) . ")");
+               if (is_object($result)) {
+                       while ($row = $result->fetch_assoc()) {
+                               $foreign_key = &$return[$row["id"]];
+                               if (!$foreign_key) {
+                                       $foreign_key = $row;
+                               }
+                               $foreign_key["source"][] = $row["from"];
+                               $foreign_key["target"][] = $row["to"];
+                       }
+               }
+               return $return;
+       }
+
+       function view($name) {
+               global $connection;
+               return array("select" => preg_replace('~^(?:[^`"[]+|`[^`]*`|"[^"]*")* AS\\s+~iU', '', $connection->result("SELECT sql FROM sqlite_master WHERE name = " . $connection->quote($name)))); //! identifiers may be inside []
+       }
+
+       function collations() {
+               return get_vals("PRAGMA collation_list", 1);
+       }
+
+       function information_schema($db) {
+               return false;
+       }
+
+       function error() {
+               global $connection;
+               return h($connection->error);
+       }
+       
+       function exact_value($val) {
+               global $connection;
+               return $connection->quote($val);
+       }
+
+       function rename_database($name, $collation) {
+               global $connection;
+               $connection->close(); //! not available with all extensions
+               return rename(DB, $name);
+       }
+       
+       function auto_increment() {
+               return " PRIMARY KEY" . (DRIVER == "sqlite" ? " AUTOINCREMENT" : "");
+       }
+       
+       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
+               global $connection;
+               $alter = array();
+               foreach ($fields as $field) {
+                       $alter[] = ($table != "" && $field[0] == "" ? "ADD " : "  ") . implode("", $field[1]);
+               }
+               $alter = array_merge($alter, $foreign);
+               if ($table != "") {
+                       foreach ($alter as $val) {
+                               if (!queries("ALTER TABLE " . idf_escape($table) . " $val")) {
+                                       return false;
+                               }
+                       }
+                       if ($table != $name && !queries("ALTER TABLE " . idf_escape($table) . " RENAME TO " . idf_escape($name))) {
+                               return false;
+                       }
+               } elseif (!queries("CREATE TABLE " . idf_escape($name) . " (\n" . implode(",\n", $alter) . "\n)")) {
+                       return false;
+               }
+               if ($auto_increment) {
+                       return queries("UPDATE sqlite_sequence SET seq = $auto_increment WHERE name = " . $connection->quote($name) . "");
+               }
+               return true;
+       }
+       
+       function alter_indexes($table, $alter) {
+               foreach ($alter as $val) {
+                       if (!queries(($val[2] ? "DROP INDEX" : "CREATE" . ($val[0] != "INDEX" ? " UNIQUE" : "") . " INDEX " . idf_escape(uniqid($table . "_")) . " ON " . idf_escape($table)) . " $val[1]")) { //! primary key must be created in CREATE TABLE
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       function truncate_tables($tables) {
+               foreach ($tables as $table) {
+                       if (!queries("DELETE FROM " . idf_escape($table))) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       function drop_views($views) {
+               foreach ($views as $view) {
+                       if (!queries("DROP VIEW " . idf_escape($view))) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       function drop_tables($tables) {
+               foreach ($tables as $table) {
+                       if (!queries("DROP TABLE " . idf_escape($table))) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+       
+       function trigger($name) {
+               global $connection;
+               preg_match('~^CREATE\\s+TRIGGER\\s*(?:[^`"\\s]+|`[^`]*`|"[^"]*")+\\s*([a-z]+)\\s+([a-z]+)\\s+ON\\s*(?:[^`"\\s]+|`[^`]*`|"[^"]*")+\\s*(?:FOR\\s*EACH\\s*ROW\\s)?(.*)~is', $connection->result("SELECT sql FROM sqlite_master WHERE name = " . $connection->quote($name)), $match);
+               return array("Timing" => strtoupper($match[1]), "Event" => strtoupper($match[2]), "Trigger" => $name, "Statement" => $match[3]);
+       }
+       
+       function triggers($table) {
+               global $connection;
+               $return = array();
+               $result = $connection->query("SELECT * FROM sqlite_master WHERE type = 'trigger' AND tbl_name = " . $connection->quote($table));
+               while ($row = $result->fetch_assoc()) {
+                       preg_match('~^CREATE\\s+TRIGGER\\s*(?:[^`"\\s]+|`[^`]*`|"[^"]*")+\\s*([a-z]+)\\s*([a-z]+)~i', $row["sql"], $match);
+                       $return[$row["name"]] = array($match[1], $match[2]);
+               }
+               return $return;
+       }
+       
+       function explain($connection, $query) {
+               return $connection->query("EXPLAIN $query");
+       }
+       
+       function create_sql($table) {
+               global $connection;
+               return $connection->result("SELECT sql FROM sqlite_master WHERE name = " . $connection->quote($table));
+       }
+       
+       function support($feature) {
+               return ereg('^(view|trigger)$', $feature);
+       }
+       
+       $driver = "sqlite";
+       $types = array("integer" => 0, "real" => 0, "numeric" => 0, "text" => 0, "blob" => 0);
+       $structured_types = array_keys($types);
+       $unsigned = array();
+       $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); // REGEXP can be user defined function
+       $functions = array("hex", "length", "lower", "round", "unixepoch", "upper");
+       $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
+       $edit_functions = array(
+               array(
+                       // "text" => "date('now')/time('now')/datetime('now')",
+               ), array(
+                       "integer|real|numeric" => "+/-",
+                       // "text" => "date/time/datetime",
+                       "text" => "||",
+               )
+       );
+}
index 12336c560c21fbbdca10c1213530d12d375c92b5..4f455ac1ae1ec02cf3884cbe0ebe02876982200f 100644 (file)
@@ -2,25 +2,37 @@
 $TABLE = $_GET["dump"];
 
 if ($_POST) {
+       $cookie = "";
+       foreach (array("output", "format", "db_style", "table_style", "data_style") as $key) {
+               $cookie .= "&$key=" . urlencode($_POST[$key]);
+       }
+       cookie("adminer_export", substr($cookie, 1));
        $ext = dump_headers(($TABLE != "" ? $TABLE : DB), (DB == "" || count((array) $_POST["tables"] + (array) $_POST["data"]) > 1));
        if ($_POST["format"] == "sql") {
-               echo "-- Adminer $VERSION dump
-SET NAMES utf8;
+               echo "-- Adminer $VERSION " . $drivers[DRIVER] . " dump
+
+" . ($driver != "sql" ? "" : "SET NAMES utf8;
 SET foreign_key_checks = 0;
-SET time_zone = " . $connection->quote($connection->result($connection->query("SELECT @@time_zone"))) . ";
+SET time_zone = " . $connection->quote($connection->result("SELECT @@time_zone")) . ";
 SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
 
-";
+");
        }
        
        $style = $_POST["db_style"];
-       foreach ((DB != "" ? array(DB) : (array) $_POST["databases"]) as $db) {
+       $databases = array(DB);
+       if (DB == "") {
+               $databases = $_POST["databases"];
+               if (is_string($databases)) {
+                       $databases = explode("\n", rtrim(str_replace("\r", "", $databases), "\n"));
+               }
+       }
+       foreach ((array) $databases as $db) {
                if ($connection->select_db($db)) {
-                       if ($_POST["format"] == "sql" && ereg('CREATE', $style) && ($result = $connection->query("SHOW CREATE DATABASE " . idf_escape($db)))) {
+                       if ($_POST["format"] == "sql" && ereg('CREATE', $style) && ($create = $connection->result("SHOW CREATE DATABASE " . idf_escape($db), 1))) {
                                if ($style == "DROP+CREATE") {
                                        echo "DROP DATABASE IF EXISTS " . idf_escape($db) . ";\n";
                                }
-                               $create = $connection->result($result, 1);
                                echo ($style == "CREATE+ALTER" ? preg_replace('~^CREATE DATABASE ~', '\\0IF NOT EXISTS ', $create) : $create) . ";\n";
                        }
                        if ($_POST["format"] == "sql") {
@@ -36,7 +48,7 @@ SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
                                                $result = $connection->query("SHOW $routine STATUS WHERE Db = " . $connection->quote($db));
                                                while ($row = $result->fetch_assoc()) {
                                                        $out .= ($style != 'DROP+CREATE' ? "DROP $routine IF EXISTS " . idf_escape($row["Name"]) . ";;\n" : "")
-                                                       . $connection->result($connection->query("SHOW CREATE $routine " . idf_escape($row["Name"])), 2) . ";;\n\n";
+                                                       . $connection->result("SHOW CREATE $routine " . idf_escape($row["Name"]), 2) . ";;\n\n";
                                                }
                                        }
                                }
@@ -45,7 +57,7 @@ SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
                                        if ($result) {
                                                while ($row = $result->fetch_assoc()) {
                                                        $out .= ($style != 'DROP+CREATE' ? "DROP EVENT IF EXISTS " . idf_escape($row["Name"]) . ";;\n" : "")
-                                                       . $connection->result($connection->query("SHOW CREATE EVENT " . idf_escape($row["Name"])), 3) . ";;\n\n";
+                                                       . $connection->result("SHOW CREATE EVENT " . idf_escape($row["Name"]), 3) . ";;\n\n";
                                                }
                                        }
                                }
@@ -56,6 +68,7 @@ SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
                        
                        if ($_POST["table_style"] || $_POST["data_style"]) {
                                $views = array();
+                               //! defer number of rows to JavaScript
                                foreach (table_status() as $row) {
                                        $table = (DB == "" || in_array($row["Name"], (array) $_POST["tables"]));
                                        $data = (DB == "" || in_array($row["Name"], (array) $_POST["data"]));
@@ -143,22 +156,26 @@ page_header(lang('Export'), "", ($_GET["export"] != "" ? array("table" => $_GET[
 $db_style = array('', 'USE', 'DROP+CREATE', 'CREATE');
 $table_style = array('', 'DROP+CREATE', 'CREATE');
 $data_style = array('', 'TRUNCATE+INSERT', 'INSERT', 'INSERT+UPDATE');
-if ($connection->server_info >= 5) {
+if (support("routine")) {
        $db_style[] = 'CREATE+ALTER';
        $table_style[] = 'CREATE+ALTER';
 }
-echo "<tr><th>" . lang('Output') . "<td><input type='hidden' name='token' value='$token'>" . $adminer->dumpOutput(0) . "\n"; // token is not needed but checked in bootstrap for all POST data //! read from cookie
-echo "<tr><th>" . lang('Format') . "<td>" . $adminer->dumpFormat(0) . "\n";
-echo "<tr><th>" . lang('Database') . "<td>" . html_select('db_style', $db_style, (DB != "" ? '' : 'CREATE'));
-if ($connection->server_info >= 5) {
-       $checked = $_GET["dump"] == "";
+parse_str($_COOKIE["adminer_export"], $row);
+if (!$row) {
+       $row = array("output" => "text", "format" => "sql", "db_style" => (DB != "" ? "" : "CREATE"), "table_style" => "DROP+CREATE", "data_style" => "INSERT");
+}
+echo "<tr><th>" . lang('Output') . "<td>" . $adminer->dumpOutput(0, $row["output"]) . "\n";
+echo "<tr><th>" . lang('Format') . "<td>" . $adminer->dumpFormat(0, $row["format"]) . "\n";
+echo "<tr><th>" . lang('Database') . "<td>" . html_select('db_style', $db_style, $row["db_style"]);
+$checked = ($_GET["dump"] == "");
+if (support("routine")) {
        echo checkbox("routines", 1, $checked, lang('Routines'));
-       if ($connection->server_info >= 5.1) {
-               echo checkbox("events", 1, $checked, lang('Events'));
-       }
 }
-echo "<tr><th>" . lang('Tables') . "<td>" . html_select('table_style', $table_style, 'DROP+CREATE');
-echo "<tr><th>" . lang('Data') . "<td>" . html_select('data_style', $data_style, 'INSERT');
+if (support("event")) {
+       echo checkbox("events", 1, $checked, lang('Events'));
+}
+echo "<tr><th>" . lang('Tables') . "<td>" . html_select('table_style', $table_style, $row["table_style"]);
+echo "<tr><th>" . lang('Data') . "<td>" . html_select('data_style', $data_style, $row["data_style"]);
 ?>
 </table>
 <p><input type="submit" value="<?php echo lang('Export'); ?>">
@@ -178,22 +195,27 @@ if (DB != "") {
                $prefix = ereg_replace("_.*", "", $name);
                $checked = ($TABLE == "" || $TABLE == (substr($TABLE, -1) == "%" ? "$prefix%" : $name)); //! % may be part of table name
                $print = "<tr><td>" . checkbox("tables[]", $name, $checked, $name, "formUncheck('check-tables');");
-               if (!$row["Engine"]) {
+               if (eregi("view", $row["Engine"])) {
                        $views .= "$print\n";
                } else {
-                       echo "$print<td align='right'><label>" . ($row["Engine"] == "InnoDB" && $row["Rows"] ? lang('~ %s', $row["Rows"]) : $row["Rows"]) . checkbox("data[]", $name, $checked, "", "formUncheck('check-data');") . "</label>\n";
+                       echo "$print<td align='right'><label>" . ($row["Engine"] == "InnoDB" && $row["Rows"] ? "~ " : "") . $row["Rows"] . checkbox("data[]", $name, $checked, "", "formUncheck('check-data');") . "</label>\n";
                }
                $prefixes[$prefix]++;
        }
        echo $views;
 } else {
        echo "<thead><tr><th style='text-align: left;'><label><input type='checkbox' id='check-databases'" . ($TABLE == "" ? " checked" : "") . " onclick='formCheck(this, /^databases\\[/);'>" . lang('Database') . "</label></thead>\n";
-       foreach (get_databases() as $db) {
-               if (!information_schema($db)) {
-                       $prefix = ereg_replace("_.*", "", $db);
-                       echo "<tr><td>" . checkbox("databases[]", $db, $TABLE == "" || $TABLE == "$prefix%", $db, "formUncheck('check-databases');") . "</label>\n";
-                       $prefixes[$prefix]++;
+       $databases = get_databases();
+       if ($databases) {
+               foreach ($databases as $db) {
+                       if (!information_schema($db)) {
+                               $prefix = ereg_replace("_.*", "", $db);
+                               echo "<tr><td>" . checkbox("databases[]", $db, $TABLE == "" || $TABLE == "$prefix%", $db, "formUncheck('check-databases');") . "</label>\n";
+                               $prefixes[$prefix]++;
+                       }
                }
+       } else {
+               echo "<tr><td><textarea name='databases' rows='10' cols='20'></textarea>";
        }
 }
 ?>
index f0fb7781b7ed8fd0dc46b0269460c2946c94ee88..a85587bf0c58dcfa0cb624d19076e8d8691c9aa2 100644 (file)
@@ -14,30 +14,22 @@ if ($_POST && !$error && !isset($_GET["select"])) {
                $location = ($update ? null : $_SERVER["REQUEST_URI"]);
        } elseif (!ereg('^.+&select=.+$', $location)) {
                $location = ME . "select=" . urlencode($TABLE);
-               $i = 0; // append &set converted to &where
-               foreach ((array) $_GET["set"] as $key => $val) {
-                       if ($val == $_POST["fields"][$key]) {
-                               $location .= where_link($i++, bracket_escape($key, "back"), $val);
-                       }
-               }
        }
        if (isset($_POST["delete"])) {
-               query_redirect("DELETE FROM " . idf_escape($_GET["edit"]) . " WHERE $where LIMIT 1", $location, lang('Item has been deleted.'));
+               query_redirect("DELETE" . limit1("FROM " . idf_escape($_GET["edit"]) . "\nWHERE $where"), $location, lang('Item has been deleted.'));
        } else {
                $set = array();
                foreach ($fields as $name => $field) {
                        $val = process_input($field);
-                       if (!$update) {
-                               $set[idf_escape($name)] = ($val !== false ? $val : "''");
-                       } elseif ($val !== false) {
-                               $set[] = "\n" . idf_escape($name) . " = $val";
+                       if ($val !== false && $val !== null) {
+                               $set[idf_escape($name)] = ($update ? "\n" . idf_escape($name) . " = $val" : $val);
                        }
                }
-               if (!$set) {
-                       redirect($location);
-               }
                if ($update) {
-                       query_redirect("UPDATE " . idf_escape($TABLE) . " SET" . implode(",", $set) . "\nWHERE $where\nLIMIT 1", $location, lang('Item has been updated.'));
+                       if (!$set) {
+                               redirect($location);
+                       }
+                       query_redirect("UPDATE" . limit1(idf_escape($TABLE) . " SET" . implode(",", $set) . "\nWHERE $where"), $location, lang('Item has been updated.'));
                } else {
                        query_redirect("INSERT INTO " . idf_escape($TABLE) . " (" . implode(", ", array_keys($set)) . ")\nVALUES (" . implode(", ", $set) . ")", $location, lang('Item has been inserted.'));
                }
@@ -64,8 +56,11 @@ if ($_POST["save"]) {
        }
        $row = array();
        if ($select) {
-               $result = $connection->query("SELECT " . implode(", ", $select) . " FROM " . idf_escape($TABLE) . " WHERE $where " . (isset($_GET["select"]) ? "HAVING COUNT(*) = 1" : "LIMIT 1"));
+               $result = $connection->query("SELECT" . limit(implode(", ", $select) . " FROM " . idf_escape($TABLE) . " WHERE $where", (isset($_GET["select"]) ? 2 : 1)));
                $row = $result->fetch_assoc();
+               if (isset($_GET["select"]) && $result->fetch_assoc()) {
+                       $row = false;
+               }
        }
 }
 ?>
@@ -80,7 +75,7 @@ if ($fields) {
                $default = $_GET["set"][bracket_escape($name)];
                $value = (isset($row)
                        ? ($row[$name] != "" && ereg("enum|set", $field["type"]) ? intval($row[$name]) : $row[$name])
-                       : ($_POST["clone"] && $field["auto_increment"] ? "" : (isset($_GET["select"]) ? false : (isset($default) ? $default : $field["default"])))
+                       : (!$update && $field["auto_increment"] ? "" : (isset($_GET["select"]) ? false : (isset($default) ? $default : $field["default"])))
                );
                if (!$_POST["save"] && is_string($value)) {
                        $value = $adminer->editVal($value, $field);
index bc117a1cc10daf09a8a3bcce1cfaa555eac37446..200d3a03d40a8cb500ade86ca65114148e167701 100644 (file)
@@ -2,7 +2,7 @@
 $TABLE = $_GET["foreign"];
 if ($_POST && !$error && !$_POST["add"] && !$_POST["change"] && !$_POST["change-js"]) {
        if ($_POST["drop"]) {
-               query_redirect("ALTER TABLE " . idf_escape($TABLE) . "\nDROP FOREIGN KEY " . idf_escape($_GET["name"]), ME . "table=" . urlencode($TABLE), lang('Foreign key has been dropped.'));
+               query_redirect("ALTER TABLE " . idf_escape($TABLE) . "\nDROP " . ($driver == "sql" ? "FOREIGN KEY " : "CONSTRAINT ") . idf_escape($_GET["name"]), ME . "table=" . urlencode($TABLE), lang('Foreign key has been dropped.'));
        } else {
                $source = array_filter($_POST["source"], 'strlen');
                ksort($source); // enforce input order
@@ -39,13 +39,19 @@ if ($_POST) {
 
 $source = array_keys(fields($TABLE)); //! no text and blob
 $target = ($TABLE === $row["table"] ? $source : array_keys(fields($row["table"])));
+$referencable = array();
+foreach (table_status() as $name => $table_status) {
+       if (fk_support($table_status)) {
+               $referencable[] = $name;
+       }
+}
 ?>
 
 <form action="" method="post">
 <p>
 <?php if ($row["db"] == "") { ?>
 <?php echo lang('Target table'); ?>:
-<?php echo html_select("table", array_keys(table_status_referencable()), $row["table"], "this.form['change-js'].value = '1'; this.form.submit();"); ?>
+<?php echo html_select("table", $referencable, $row["table"], "this.form['change-js'].value = '1'; this.form.submit();"); ?>
 <input type="hidden" name="change-js" value="">
 <noscript><p><input type="submit" name="change" value="<?php echo lang('Change'); ?>"></noscript>
 <table cellspacing="0">
index ccbbe775bf961c0cf2092c57788e0aa30ccb6926..895793df8698b7ecd084f50fd7955c569e106418 100644 (file)
@@ -1,13 +1,7 @@
 <?php
 class Adminer {
-       /** @var array functions used in select */
-       var $functions = array("char_length", "from_unixtime", "hex", "lower", "round", "sec_to_time", "time_to_sec", "unix_timestamp", "upper");
-       
-       /** @var array grouping functions used in select */
-       var $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
-       
-       /** @var array operators used in select */
-       var $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "REGEXP", "IN", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL");
+       /** @var array operators used in select, null for all operators */
+       var $operators;
        
        /** Name in title and navigation
        * @return string
@@ -20,7 +14,7 @@ class Adminer {
        * @return array ($server, $username, $password)
        */
        function credentials() {
-               return array($_GET["server"], $_SESSION["usernames"][$_GET["server"]], $_SESSION["passwords"][$_GET["server"]]);
+               return array(SERVER, $_GET["username"], get_session("passwords"));
        }
        
        /** Get key used for permanent login
@@ -39,14 +33,15 @@ class Adminer {
        }
        
        /** Print login form
-       * @param string
        * @return null
        */
-       function loginForm($username) {
+       function loginForm() {
+               global $drivers, $possible_drivers;
                ?>
 <table cellspacing="0">
-<tr><th><?php echo lang('Server'); ?><td><input name="server" value="<?php echo h($_GET["server"]); ?>">
-<tr><th><?php echo lang('Username'); ?><td><input name="username" value="<?php echo h($username); ?>">
+<tr><th><?php echo lang('System'); ?><td><?php echo (count($possible_drivers) > 3 ? html_select("driver", $drivers, DRIVER) : "<input type='hidden' name='driver' value='" . key($drivers) . "'>" . reset($drivers)); ?></tr>
+<tr><th><?php echo lang('Server'); ?><td><input name="server" value="<?php echo h(SERVER); ?>">
+<tr><th><?php echo lang('Username'); ?><td><input name="username" value="<?php echo h($_GET["username"]); ?>">
 <tr><th><?php echo lang('Password'); ?><td><input type="password" name="password">
 </table>
 <?php
@@ -90,10 +85,10 @@ class Adminer {
        function selectLinks($tableStatus, $set = "") {
                echo '<p class="tabs">';
                $links = array("select" => lang('Select data'), "table" => lang('Show structure'));
-               if (isset($tableStatus["Rows"])) {
-                       $links["create"] = lang('Alter table');
-               } else {
+               if (eregi("view", $tableStatus["Engine"])) {
                        $links["view"] = lang('Alter view');
+               } else {
+                       $links["create"] = lang('Alter table');
                }
                if (isset($set)) {
                        $links["edit"] = lang('New item');
@@ -126,7 +121,8 @@ class Adminer {
        * @return string
        */
        function selectQuery($query) {
-               return "<p><code class='jush-sql'>" . h(str_replace("\n", " ", $query)) . "</code> <a href='" . h(ME) . "sql=" . urlencode($query) . "'>" . lang('Edit') . "</a>\n";
+               global $driver;
+               return "<p><code class='jush-$driver'>" . h(str_replace("\n", " ", $query)) . "</code> <a href='" . h(ME) . "sql=" . urlencode($query) . "'>" . lang('Edit') . "</a>\n";
        }
        
        /** Description of a row in a table
@@ -154,7 +150,7 @@ class Adminer {
        */
        function selectVal($val, $link, $field) {
                $return = ($val != "<i>NULL</i>" && $field["type"] == "char" ? "<code>$val</code>" : $val);
-               if (ereg('blob|binary', $field["type"]) && !is_utf8($val)) {
+               if (ereg('binary|blob|bytea', $field["type"]) && !is_utf8($val)) {
                        $return = lang('%d byte(s)', strlen($val));
                }
                return ($link ? "<a href='$link'>$return</a>" : $return);
@@ -175,17 +171,18 @@ class Adminer {
        * @return null
        */
        function selectColumnsPrint($select, $columns) {
+               global $functions, $grouping;
                print_fieldset("select", lang('Select'), $select);
                $i = 0;
-               $fun_group = array(lang('Functions') => $this->functions, lang('Aggregation') => $this->grouping);
+               $fun_group = array(lang('Functions') => $functions, lang('Aggregation') => $grouping);
                foreach ($select as $key => $val) {
                        $val = $_GET["columns"][$key];
                        echo "<div>" . html_select("columns[$i][fun]", array(-1 => "") + $fun_group, $val["fun"]);
-                       echo "<select name='columns[$i][col]'><option>" . optionlist($columns, $val["col"], true) . "</select></div>\n";
+                       echo "(<select name='columns[$i][col]'><option>" . optionlist($columns, $val["col"], true) . "</select>)</div>\n";
                        $i++;
                }
-               echo "<div>" . html_select("columns[$i][fun]", array(-1 => "") + $fun_group, "", "this.nextSibling.onchange();");
-               echo "<select name='columns[$i][col]' onchange='selectAddRow(this);'><option>" . optionlist($columns, null, true) . "</select></div>\n";
+               echo "<div>" . html_select("columns[$i][fun]", array(-1 => "") + $fun_group, "", "this.nextSibling.nextSibling.onchange();");
+               echo "(<select name='columns[$i][col]' onchange='selectAddRow(this);'><option>" . optionlist($columns, null, true) . "</select>)</div>\n";
                echo "</div></fieldset>\n";
        }
        
@@ -208,13 +205,13 @@ class Adminer {
                $i = 0;
                foreach ((array) $_GET["where"] as $val) {
                        if ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators)) {
-                               echo "<div><select name='where[$i][col]'><option value=''>" . lang('(anywhere)') . optionlist($columns, $val["col"], true) . "</select>";
+                               echo "<div><select name='where[$i][col]'><option value=''>(" . lang('anywhere') . ")" . optionlist($columns, $val["col"], true) . "</select>";
                                echo html_select("where[$i][op]", $this->operators, $val["op"]);
                                echo "<input name='where[$i][val]' value='" . h($val["val"]) . "'></div>\n";
                                $i++;
                        }
                }
-               echo "<div><select name='where[$i][col]' onchange='selectAddRow(this);'><option value=''>" . lang('(anywhere)') . optionlist($columns, null, true) . "</select>";
+               echo "<div><select name='where[$i][col]' onchange='selectAddRow(this);'><option value=''>(" . lang('anywhere') . ")" . optionlist($columns, null, true) . "</select>";
                echo html_select("where[$i][op]", $this->operators);
                echo "<input name='where[$i][val]'></div>\n";
                echo "</div></fieldset>\n";
@@ -286,12 +283,13 @@ class Adminer {
        * @return array (array(select_expressions), array(group_expressions))
        */
        function selectColumnsProcess($columns, $indexes) {
+               global $functions, $grouping;
                $select = array(); // select expressions, empty for *
                $group = array(); // expressions without aggregation - will be used for GROUP BY if an aggregation function is used
                foreach ((array) $_GET["columns"] as $key => $val) {
-                       if ($val["fun"] == "count" || (isset($columns[$val["col"]]) && (!$val["fun"] || in_array($val["fun"], $this->functions) || in_array($val["fun"], $this->grouping)))) {
+                       if ($val["fun"] == "count" || (isset($columns[$val["col"]]) && (!$val["fun"] || in_array($val["fun"], $functions) || in_array($val["fun"], $grouping)))) {
                                $select[$key] = apply_sql_function($val["fun"], (isset($columns[$val["col"]]) ? idf_escape($val["col"]) : "*"));
-                               if (!in_array($val["fun"], $this->grouping)) {
+                               if (!in_array($val["fun"], $grouping)) {
                                        $group[] = $select[$key];
                                }
                        }
@@ -314,8 +312,15 @@ class Adminer {
                }
                foreach ((array) $_GET["where"] as $val) {
                        if ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators)) {
-                               $in = process_length($val["val"]);
-                               $cond = " $val[op]" . (ereg('NULL$', $val["op"]) ? "" : (ereg('IN$', $val["op"]) ? " (" . ($in != "" ? $in : "NULL") . ")" : " " . $this->processInput($fields[$val["col"]], $val["val"])));
+                               $cond = " $val[op]";
+                               if (ereg('IN$', $val["op"])) {
+                                       $in = process_length($val["val"]);
+                                       $cond .= " (" . ($in != "" ? $in : "NULL") . ")";
+                               } elseif ($val["op"] == "LIKE %%") {
+                                       $cond = " LIKE " . $this->processInput($fields[$val["col"]], "%$val[val]%");
+                               } elseif (!ereg('NULL$', $val["op"])) {
+                                       $cond .= " " . $this->processInput($fields[$val["col"]], $val["val"]);
+                               }
                                if ($val["col"] != "") {
                                        $return[] = idf_escape($val["col"]) . $cond;
                                } else {
@@ -342,8 +347,8 @@ class Adminer {
        function selectOrderProcess($fields, $indexes) {
                $return = array();
                foreach ((array) $_GET["order"] as $key => $val) {
-                       if (isset($fields[$val]) || preg_match('~^((COUNT\\(DISTINCT |[A-Z0-9_]+\\()`(?:[^`]|``)+`\\)|COUNT\\(\\*\\))$~', $val)) {
-                               $return[] = idf_escape($val) . (isset($_GET["desc"][$key]) ? " DESC" : "");
+                       if (isset($fields[$val]) || preg_match('~^((COUNT\\(DISTINCT |[A-Z0-9_]+\\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\\)|COUNT\\(\\*\\))$~', $val)) { //! MS SQL uses []
+                               $return[] = (isset($fields[$val]) ? idf_escape($val) : $val) . (isset($_GET["desc"][$key]) ? " DESC" : "");
                        }
                }
                return $return;
@@ -377,10 +382,12 @@ class Adminer {
        * @return string
        */
        function messageQuery($query) {
+               global $driver;
                restart_session();
                $id = "sql-" . count($_SESSION["messages"]);
-               $_SESSION["history"][$_GET["server"]][DB][] = (strlen($query) > 1e6 ? ereg_replace('[\x80-\xFF]+$', '', substr($query, 0, 1e6)) . "\n..." : $query); // [\x80-\xFF] - valid UTF-8, \n - can end by one-line comment
-               return " <a href='#$id' onclick=\"return !toggle('$id');\">" . lang('SQL command') . "</a><div id='$id' class='hidden'><pre class='jush-sql'>" . shorten_utf8($query, 1000) . '</pre><a href="' . h(ME . 'sql=&history=' . (count($_SESSION["history"][$_GET["server"]][DB]) - 1)) . '">' . lang('Edit') . '</a></div>';
+               $history = &get_session("history");
+               $history[DB][] = (strlen($query) > 1e6 ? ereg_replace('[\x80-\xFF]+$', '', substr($query, 0, 1e6)) . "\n..." : $query); // [\x80-\xFF] - valid UTF-8, \n - can end by one-line comment
+               return " <a href='#$id' onclick=\"return !toggle('$id');\">" . lang('SQL command') . "</a><div id='$id' class='hidden'><pre class='jush-$driver'>" . shorten_utf8($query, 1000) . '</pre><p><a href="' . h(ME . 'sql=&history=' . (count($history[DB]) - 1)) . '">' . lang('Edit') . '</a></div>';
        }
        
        /** Functions displayed in edit form
@@ -388,31 +395,18 @@ class Adminer {
        * @return array
        */
        function editFunctions($field) {
-               $return = array("");
-               if (ereg('char|date|time', $field["type"])) {
-                       $return = (ereg('char', $field["type"]) ? array("", "md5", "sha1", "password", "encrypt", "uuid") : array("", "now")); //! JavaScript for disabling maxlength
-               }
-               if (!isset($_GET["call"]) && (isset($_GET["select"]) || where($_GET))) {
-                       // relative functions
-                       if (ereg('int|float|double|decimal', $field["type"])) {
-                               $return = array("", "+", "-");
-                       }
-                       if (ereg('date', $field["type"])) {
-                               $return[] = "+ interval";
-                               $return[] = "- interval";
-                       }
-                       if (ereg('time', $field["type"])) {
-                               $return[] = "addtime";
-                               $return[] = "subtime";
-                       }
-                       if (ereg('char|text', $field["type"])) {
-                               $return[] = "concat";
+               global $edit_functions;
+               $return = ($field["null"] ? "/NULL" : "");
+               foreach ($edit_functions as $key => $functions) {
+                       if (!$key || (!isset($_GET["call"]) && (isset($_GET["select"]) || where($_GET)))) { // relative functions
+                               foreach ($functions as $pattern => $val) {
+                                       if (!$pattern || ereg($pattern, $field["type"])) {
+                                               $return .= "/$val";
+                                       }
+                               }
                        }
                }
-               if ($field["null"]) {
-                       array_unshift($return, "NULL");
-               }
-               return $return;
+               return explode("/", $return);
        }
        
        /** Get options to display edit field
@@ -441,9 +435,9 @@ class Adminer {
                global $connection;
                $name = $field["field"];
                $return = $connection->quote($value);
-               if (ereg('^(now|uuid)$', $function)) {
+               if (ereg('^(now|getdate|uuid)$', $function)) {
                        $return = "$function()";
-               } elseif (ereg('^[+-]$', $function)) {
+               } elseif (ereg('^([+-]|\\|\\|)$', $function)) {
                        $return = idf_escape($name) . " $function $return";
                } elseif (ereg('^[+-] interval$', $function)) {
                        $return = idf_escape($name) . " $function " . (preg_match("~^([0-9]+|'[0-9.: -]') [A-Z_]+$~i", $value) ? $value : $return);
@@ -457,9 +451,10 @@ class Adminer {
        
        /** Returns export output options
        * @param bool generate select (otherwise radio)
+       * @param string
        * @return string
        */
-       function dumpOutput($select) {
+       function dumpOutput($select, $value = "") {
                $return = array('text' => lang('open'), 'file' => lang('save'));
                if (function_exists('gzencode')) {
                        $return['gz'] = 'gzip';
@@ -468,15 +463,16 @@ class Adminer {
                        $return['bz2'] = 'bzip2';
                }
                // ZipArchive requires temporary file, ZIP can be created by gzcompress - see PEAR File_Archive
-               return html_select("output", $return, "text", $select);
+               return html_select("output", $return, $value, $select);
        }
        
        /** Returns export format options
        * @param bool generate select (otherwise radio)
+       * @param string
        * @return string
        */
-       function dumpFormat($select) {
-               return html_select("format", array('sql' => 'SQL', 'csv' => 'CSV'), "sql", $select);
+       function dumpFormat($select, $value = "") {
+               return html_select("format", array('sql' => 'SQL', 'csv' => 'CSV,', 'csv;' => 'CSV;'), $value, $select);
        }
        
        /** Prints navigation after Adminer title
@@ -484,7 +480,7 @@ class Adminer {
        * @return null
        */
        function navigation($missing) {
-               global $VERSION, $connection;
+               global $VERSION, $connection, $token;
                ?>
 <h1>
 <a href="http://www.adminer.org/" id="h1"><?php echo $this->name(); ?></a>
@@ -499,19 +495,18 @@ class Adminer {
 <p class="logout">
 <a href="<?php echo h(ME); ?>sql="><?php echo bold(lang('SQL command'), isset($_GET["sql"])); ?></a>
 <a href="<?php echo h(ME); ?>dump=<?php echo urlencode(isset($_GET["table"]) ? $_GET["table"] : $_GET["select"]); ?>"><?php echo bold(lang('Dump'), isset($_GET["dump"])); ?></a>
-<input type="hidden" name="token" value="<?php echo $_SESSION["tokens"][$_GET["server"]]; ?>">
+<input type="hidden" name="token" value="<?php echo $token; ?>">
 <input type="submit" name="logout" value="<?php echo lang('Logout'); ?>">
 </p>
 </form>
 <form action="">
 <p>
-<?php echo SID_FORM; ?>
-<?php if ($_GET["server"] != "") { ?><input type="hidden" name="server" value="<?php echo h($_GET["server"]); ?>"><?php } ?>
+<?php hidden_fields_get(); ?>
 <?php echo ($databases ? html_select("db", array("" => "(" . lang('database') . ")") + $databases, DB, "this.form.submit();") : '<input name="db" value="' . h(DB) . '">'); ?>
 <?php if (isset($_GET["sql"])) { ?><input type="hidden" name="sql" value=""><?php } ?>
 <?php if (isset($_GET["schema"])) { ?><input type="hidden" name="schema" value=""><?php } ?>
 <?php if (isset($_GET["dump"])) { ?><input type="hidden" name="dump" value=""><?php } ?>
- <input type="submit" value="<?php echo lang('Use'); ?>"<?php echo ($databases ? " class='hidden'" : ""); ?>>
+<input type="submit" value="<?php echo lang('Use'); ?>"<?php echo ($databases ? " class='hidden'" : ""); ?>>
 </p>
 </form>
 <?php
@@ -533,7 +528,7 @@ class Adminer {
        */
        function tablesPrint($tables) {
                echo "<p id='tables'>\n";
-               foreach ($tables as $table) {
+               foreach ($tables as $table => $type) {
                        echo '<a href="' . h(ME) . 'select=' . urlencode($table) . '">' . bold(lang('select'), $_GET["select"] == $table) . '</a> ';
                        echo '<a href="' . h(ME) . 'table=' . urlencode($table) . '">' . bold($this->tableName(array("Name" => $table)), $_GET["table"] == $table) . "</a><br>\n"; //! Adminer::tableName may work with full table status
                }
@@ -542,3 +537,6 @@ class Adminer {
 }
 
 $adminer = (function_exists('adminer_object') ? adminer_object() : new Adminer);
+if (!isset($adminer->operators)) {
+       $adminer->operators = $operators;
+}
index bc6bb55086a3f8e67396df69fad476cbe90f72b5..592cbce4a7d133e08606b27668ae58186e7da9f1 100644 (file)
 <?php
+$connection = '';
+
+if (!$drivers) {
+       page_header(lang('No extension'), lang('None of the supported PHP extensions (%s) are available.', implode(", ", $possible_drivers)), null);
+       page_footer("auth");
+       exit;
+}
+
+$token = $_SESSION["token"];
+if (!$_SESSION["token"]) {
+       $_SESSION["token"] = rand(1, 1e6); // defense against cross-site request forgery
+}
+
 if (isset($_POST["server"])) {
        session_regenerate_id(); // defense against session fixation
-       $_SESSION["usernames"][$_POST["server"]] = $_POST["username"];
-       $_SESSION["passwords"][$_POST["server"]] = $_POST["password"];
+       $_SESSION["passwords"][$_POST["driver"]][$_POST["server"]][$_POST["username"]] = $_POST["password"];
        if ($_POST["permanent"]) {
-               cookie("adminer_permanent",
+               cookie("adminer_permanent", //! store separately for each driver, server and username to allow several permanent logins
                        base64_encode($_POST["server"])
                        . ":" . base64_encode($_POST["username"])
                        . ":" . base64_encode(encrypt_string($_POST["password"], $adminer->permanentLogin()))
+                       . ":" . base64_encode($_POST["driver"])
                );
        }
-       if (count($_POST) == ($_POST["permanent"] ? 4 : 3)) { // 3 - server, username, password
-               $location = ((string) $_GET["server"] === $_POST["server"] ? remove_from_uri(session_name()) : preg_replace('~^([^?]*).*~', '\\1', ME) . ($_POST["server"] != "" ? '?server=' . urlencode($_POST["server"]) : ''));
-               if (SID_FORM) {
-                       $pos = strpos($location, '?');
-                       $location = ($pos ? substr_replace($location, SID . "&", $pos + 1, 0) : "$location?" . SID);
-               }
-               redirect($location);
+       if (count($_POST) == ($_POST["permanent"] ? 5 : 4) // 4 - driver, server, username, password
+               || DRIVER != $_POST["driver"]
+               || SERVER != $_POST["server"]
+               || $_GET["username"] !== $_POST["username"] // "0" == "00"
+       ) {
+               preg_match('~([^?]*)\\??(.*)~', remove_from_uri(implode("|", array_keys($drivers)) . "|username|" . session_name()), $match);
+               redirect("$match[1]?"
+                       . (SID ? SID . "&" : "")
+                       . ($_POST["driver"] != "server" || $_POST["server"] != "" ? urlencode($_POST["driver"]) . "=" . urlencode($_POST["server"]) . "&" : "")
+                       . "username=" . urlencode($_POST["username"])
+                       . ($match[2] ? "&$match[2]" : "")
+               );
        }
-       $_GET["server"] = $_POST["server"]; //! used also in ME
 } elseif ($_POST["logout"]) {
-       $token = $_SESSION["tokens"][$_GET["server"]];
        if ($token && $_POST["token"] != $token) {
                page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.'));
                page_footer("db");
                exit;
        } else {
-               foreach (array("usernames", "passwords", "databases", "tokens", "history") as $val) {
-                       unset($_SESSION[$val][$_GET["server"]]);
-               }
-               if (!isset($_SESSION["passwords"])) { // don't require login to logout
-                       $_SESSION["passwords"] = array();
+               foreach (array("passwords", "databases", "history") as $key) {
+                       set_session($key, null);
                }
                cookie("adminer_permanent", "");
-               redirect(substr(preg_replace('~db=[^&]*&~', '', ME), 0, -1), lang('Logout successful.'));
+               redirect(substr(preg_replace('~(username|db)=[^&]*&~', '', ME), 0, -1), lang('Logout successful.'));
        }
-} elseif ($_COOKIE["adminer_permanent"] && !isset($_SESSION["usernames"][$_GET["server"]])) {
-       list($server, $username, $cipher) = array_map('base64_decode', explode(":", $_COOKIE["adminer_permanent"]));
-       if (($_GET["server"] == "" && !$_POST) || $server == $_GET["server"]) {
+} elseif ($_COOKIE["adminer_permanent"]) {
+       list($server, $username, $cipher, $system) = array_map('base64_decode', explode(":", $_COOKIE["adminer_permanent"])); // $driver is a global variable
+       if ($server == SERVER && $username === $_GET["username"] && $system == DRIVER) {
                session_regenerate_id(); // defense against session fixation
-               $_SESSION["usernames"][$server] = $username;
-               $_SESSION["passwords"][$server] = decrypt_string($cipher, $adminer->permanentLogin());
-               if ($server != $_GET["server"]) {
-                       redirect(preg_replace('~^([^?]*).*~', '\\1', ME) . '?server=' . urlencode($server));
-               }
+               set_session("passwords", decrypt_string($cipher, $adminer->permanentLogin()));
        }
+       //! redirect ?select=tab
 }
 
 function auth_error($exception = null) {
-       global $connection, $adminer;
+       global $connection, $adminer, $token;
        $session_name = session_name();
-       $username = $_SESSION["usernames"][$_GET["server"]];
-       unset($_SESSION["usernames"][$_GET["server"]]);
-       page_header(lang('Login'), (isset($username) ? h($exception ? $exception->getMessage() : (is_string($connection) ? $connection : lang('Invalid credentials.')))
-               : (!$_COOKIE[$session_name] && $_GET[$session_name] && ini_get("session.use_only_cookies") ? lang('Session support must be enabled.')
-               : (($_COOKIE[$session_name] || $_GET[$session_name]) && !isset($_SESSION["passwords"]) ? lang('Session expired, please login again.')
-       : ""))), null);
+       $error = "";
+       if (!$_COOKIE[$session_name] && $_GET[$session_name] && ini_bool("session.use_only_cookies")) {
+               $error = lang('Session support must be enabled.');
+       } elseif (isset($_GET["username"])) {
+               if (($_COOKIE[$session_name] || $_GET[$session_name]) && !$token) {
+                       $error = lang('Session expired, please login again.');
+               } else {
+                       $password = get_session("passwords");
+                       if (isset($password)) {
+                               $error = h($exception ? $exception->getMessage() : (is_string($connection) ? $connection : lang('Invalid credentials.')));
+                       }
+               }
+       }
+       page_header(lang('Login'), $error, null);
        echo "<form action='' method='post'>\n";
-       $adminer->loginForm($username);
+       $adminer->loginForm();
        echo "<div>";
-       hidden_fields($_POST, array("server", "username", "password")); // expired session
+       hidden_fields($_POST, array("driver", "server", "username", "password", "permanent")); // expired session
        echo "</div>\n";
        echo "</form>\n";
        page_footer("auth");
 }
 
-$username = &$_SESSION["usernames"][$_GET["server"]];
-if (!isset($username)) {
-       $username = $_GET["username"]; // default username can be passed in URL
+if (isset($_GET["username"]) && class_exists("Min_DB")) { // doesn't exists with passing wrong driver
+       $connection = connect();
 }
-$connection = (isset($username) ? connect() : '');
-if (is_string($connection) || !$adminer->login($username, $_SESSION["passwords"][$_GET["server"]])) {
+if (is_string($connection) || !$adminer->login($_GET["username"], get_session("passwords"))) {
        auth_error();
        exit;
 }
-unset($username);
 
-if (!$_SESSION["tokens"][$_GET["server"]]) {
-       $_SESSION["tokens"][$_GET["server"]] = rand(1, 1e6); // defense against cross-site request forgery
-}
+$token = $_SESSION["token"]; ///< @var string CSRF protection
 if (isset($_POST["server"]) && $_POST["token"]) {
-       $_POST["token"] = $_SESSION["tokens"][$_GET["server"]];
+       $_POST["token"] = $token; // reset token after explicit login
 }
-$token = $_SESSION["tokens"][$_GET["server"]]; ///< @var string CSRF protection
 $error = ($_POST ///< @var string
        ? ($_POST["token"] == $token ? "" : lang('Invalid CSRF token. Send the form again.'))
        : ($_SERVER["REQUEST_METHOD"] != "POST" ? "" : lang('Too big POST data. Reduce the data or increase the %s configuration directive.', '"post_max_size"')) // posted form with no data means that post_max_size exceeded because Adminer always sends token at least
index 38668f4c627eed8b6e99ede1df85c5b704d4a914..6614718d399f850c6226e6560f88c8f226b1087c 100644 (file)
@@ -39,12 +39,14 @@ if (isset($_GET["file"])) {
        exit;
 }
 
+include "../adminer/include/functions.inc.php";
+
 if (!isset($_SERVER["REQUEST_URI"])) {
-       $_SERVER["REQUEST_URI"] = $_SERVER["ORIG_PATH_INFO"] . ($_SERVER["QUERY_STRING"] != "" ? "?$_SERVER[QUERY_STRING]" : "");
+       $_SERVER["REQUEST_URI"] = $_SERVER["ORIG_PATH_INFO"] . ($_SERVER["QUERY_STRING"] != "" ? "?$_SERVER[QUERY_STRING]" : ""); // IIS 5 compatibility
 }
 
 @ini_set("session.use_trans_sid", false); // protect links in export, @ - may be disabled
-if (!ini_get("session.auto_start")) {
+if (!ini_bool("session.auto_start")) {
        session_name("adminer_sid"); // use specific session name to get own namespace
        $params = array(0, preg_replace('~\\?.*~', '', $_SERVER["REQUEST_URI"]), "", $_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off"));
        if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
@@ -75,19 +77,26 @@ if (function_exists("set_magic_quotes_runtime")) {
 }
 @set_time_limit(0); // @ - can be disabled
 
-include "../adminer/include/version.inc.php";
-include "../adminer/include/functions.inc.php";
+include "../adminer/include/lang.inc.php";
+include "../adminer/lang/$LANG.inc.php";
+include "../adminer/include/pdo.inc.php";
+include "../adminer/drivers/sqlite.inc.php";
+include "../adminer/drivers/pgsql.inc.php";
+include "../adminer/drivers/mssql.inc.php";
+include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
 
+define("SERVER", $_GET[DRIVER]); // read from pgsql=localhost
 define("DB", $_GET["db"]); // for the sake of speed and size
-define("SID_FORM", SID && !ini_get("session.use_only_cookies") ? '<input type="hidden" name="' . session_name() . '" value="' . h(session_id()) . '">' : '');
-define("ME", preg_replace('~^[^?]*/([^?]*).*~', '\\1', $_SERVER["REQUEST_URI"]) . '?' . (SID_FORM ? SID . '&' : '') . ($_GET["server"] != "" ? 'server=' . urlencode($_GET["server"]) . '&' : '') . (DB != "" ? 'db=' . urlencode(DB) . '&' : ''));
+define("ME", preg_replace('~^[^?]*/([^?]*).*~', '\\1', $_SERVER["REQUEST_URI"]) . '?'
+       . (SID && !$_COOKIE ? SID . '&' : '') // !$_COOKIE - don't pass SID with permanent login
+       . (SERVER !== null ? DRIVER . "=" . urlencode(SERVER) . '&' : '')
+       . (isset($_GET["username"]) ? "username=" . urlencode($_GET["username"]) . '&' : '')
+       . (DB != "" ? 'db=' . urlencode(DB) . '&' : '')
+);
 
-include "../adminer/include/lang.inc.php";
-include "../adminer/lang/$LANG.inc.php";
+include "../adminer/include/version.inc.php";
 include "./include/adminer.inc.php";
 include "../adminer/include/design.inc.php";
-include "../adminer/include/pdo.inc.php";
-include "../adminer/include/mysql.inc.php";
 include "../adminer/include/xxtea.inc.php";
 include "../adminer/include/auth.inc.php";
 include "./include/connect.inc.php";
@@ -95,7 +104,7 @@ include "./include/editing.inc.php";
 include "./include/export.inc.php";
 
 session_cache_limiter(""); // to allow restarting session
-if (!ini_get("session.use_cookies") || @ini_set("session.use_cookies", false) !== false) { // @ - may be disabled
+if (!ini_bool("session.use_cookies") || @ini_set("session.use_cookies", false) !== false) { // @ - may be disabled
        session_write_close(); // improves concurrency if a user opens several pages at once, may be restarted later
 }
 
index 7ebdbf93d08c48d2f9f5ed109ef8fbd6a556605e..9bab9fc8f5f5f39e72fc017b2c2c9370933a3695 100644 (file)
@@ -1,11 +1,12 @@
 <?php
 function connect_error() {
-       global $connection, $VERSION, $token, $error;
+       global $connection, $VERSION, $token, $error, $drivers;
+       $databases = array();
        if (DB != "") {
-               page_header(lang('Database') . ": " . h(DB), lang('Invalid database.'), false);
+               page_header(lang('Database') . ": " . h(DB), lang('Invalid database.'), true);
        } else {
                if ($_POST["db"] && !$error) {
-                       unset($_SESSION["databases"][$_GET["server"]]);
+                       set_session("databases", null);
                        foreach ($_POST["db"] as $db) {
                                if (!queries("DROP DATABASE " . idf_escape($db))) {
                                        break;
@@ -14,30 +15,32 @@ function connect_error() {
                        queries_redirect(substr(ME, 0, -1), lang('Database has been dropped.'), !$connection->error);
                }
                
-               page_header(lang('Select database'), $error, null);
-               echo "<p>";
+               page_header(lang('Select database'), $error, false);
+               echo "<p><a href='" . h(ME) . "database='>" . lang('Create new database') . "</a>\n";
                foreach (array(
-                       'database' => lang('Create new database'),
                        'privileges' => lang('Privileges'),
                        'processlist' => lang('Process list'),
                        'variables' => lang('Variables'),
                        'status' => lang('Status'),
                ) as $key => $val) {
-                       echo "<a href='" . h(ME) . "$key='>$val</a>\n";
+                       if (support($key)) {
+                               echo "<a href='" . h(ME) . "$key='>$val</a>\n";
+                       }
                }
-               echo "<p>" . lang('MySQL version: %s through PHP extension %s', "<b" . ($connection->server_info < 4.1 ? " class='binary'" : "") . ">$connection->server_info</b>", "<b>$connection->extension</b>") . "\n";
-               echo "<p>" . lang('Logged as: %s', "<b>" . h($connection->result($connection->query("SELECT USER()"))) . "</b>") . "\n";
+               echo "<p>" . lang('%s version: %s through PHP extension %s', $drivers[DRIVER], "<b>$connection->server_info</b>", "<b>$connection->extension</b>") . "\n";
+               echo "<p>" . lang('Logged as: %s', "<b>" . h(logged_user()) . "</b>") . "\n";
                $databases = get_databases();
                if ($databases) {
                        $collations = collations();
                        echo "<form action='' method='post'>\n";
                        echo "<table cellspacing='0' onclick='tableClick(event);'>\n";
-                       echo "<thead><tr><td><input type='hidden' name='token' value='$token'>&nbsp;<th>" . lang('Database') . "<td>" . lang('Collation') . "</thead>\n";
+                       echo "<thead><tr><td><input type='hidden' name='token' value='$token'>&nbsp;<th>" . lang('Database') . "<td>" . lang('Collation') . "<td>" . lang('Tables') . "</thead>\n";
                        foreach ($databases as $db) {
                                $root = h(ME) . "db=" . urlencode($db);
                                echo "<tr" . odd() . "><td>" . checkbox("db[]", $db, in_array($db, (array) $_POST["db"]));
                                echo "<th><a href='$root'>" . h($db) . "</a>";
                                echo "<td><a href='$root&amp;database='>" . nbsp(db_collation($db, $collations)) . "</a>";
+                               echo "<td align='right'><a href='$root&amp;schema=' id='tables-" . h($db) . "'>?</a>";
                                echo "\n";
                        }
                        echo "</table>\n";
@@ -46,6 +49,11 @@ function connect_error() {
                }
        }
        page_footer("db");
+       echo "<script type='text/javascript'>\n";
+       foreach (count_tables($databases) as $db => $val) {
+               echo "setHtml('tables-" . addcslashes($db, "\\'/") . "', '$val');\n";
+       }
+       echo "</script>\n";
 }
 
 if (isset($_GET["status"])) {
@@ -53,7 +61,7 @@ if (isset($_GET["status"])) {
 }
 if (!(DB != "" ? $connection->select_db(DB) : isset($_GET["sql"]) || isset($_GET["dump"]) || isset($_GET["database"]) || isset($_GET["processlist"]) || isset($_GET["privileges"]) || isset($_GET["user"]) || isset($_GET["variables"]))) {
        if (DB != "") {
-               unset($_SESSION["databases"][$_GET["server"]]);
+               set_session("databases", null);
        }
        connect_error(); // separate function to catch SQLite error
        exit;
index 09c41048f2cbef67f001c3a39fd05aeb1bdf9f7a..a0b394777200c16705272865da8b3716d58103d9 100644 (file)
@@ -1,8 +1,15 @@
 <?php
+/** Print HTML header
+* @param string used in title, breadcrumb and heading
+* @param string
+* @param mixed array("key" => "link=desc", "key2" => array("link", "desc")), null for nothing, false for driver only, true for driver and server
+* @param string used after colon in title and heading
+* @return null
+*/
 function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
-       global $LANG, $VERSION, $adminer, $connection;
+       global $LANG, $VERSION, $adminer, $connection, $drivers;
        header("Content-Type: text/html; charset=utf-8");
-       header("X-Frame-Options: deny"); // ClickJacking protection in IE8, Safari 4, Chrome 2, NoScript plugin
+       header("X-Frame-Options: deny"); // ClickJacking protection in IE8, Safari 4, Chrome 2, Firefox NoScript plugin
        $title_all = $title . ($title2 != "" ? ": " . h($title2) : "");
        ?>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
@@ -10,34 +17,41 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <meta http-equiv="Content-Script-Type" content="text/javascript">
 <meta name="robots" content="noindex">
-<title><?php echo $title_all . ($_GET["server"] != "" && $_GET["server"] != "localhost" ? h(" - $_GET[server]") : "") . " - " . $adminer->name(); ?></title>
+<title><?php echo $title_all . (SERVER != "" && SERVER != "localhost" ? h(" - " . SERVER) : "") . " - " . $adminer->name(); ?></title>
 <link rel="shortcut icon" type="image/x-icon" href="../adminer/static/favicon.ico">
 <link rel="stylesheet" type="text/css" href="../adminer/static/default.css<?php // Ondrej Valka, http://valka.info ?>">
 <?php if (file_exists("adminer.css")) { ?>
 <link rel="stylesheet" type="text/css" href="adminer.css">
 <?php } ?>
 
-<body onload="bodyLoad('<?php echo substr($connection->server_info, 0, 3); ?>');<?php echo (isset($_COOKIE["adminer_version"]) ? "" : " verifyVersion();"); ?>">
+<body onload="bodyLoad('<?php echo (is_object($connection) ? substr($connection->server_info, 0, 3) : ""); ?>');<?php echo (isset($_COOKIE["adminer_version"]) ? "" : " verifyVersion();"); ?>">
 <script type="text/javascript" src="../adminer/static/functions.js"></script>
 <script type="text/javascript" src="static/editing.js"></script>
 
 <div id="content">
 <?php
        if (isset($breadcrumb)) {
+               $link = substr(preg_replace('~(username|db)=[^&]*&~', '', ME), 0, -1);
+               echo '<p id="breadcrumb"><a href="' . ($link ? h($link) : ".") . '">' . $drivers[DRIVER] . '</a> &raquo; ';
                $link = substr(preg_replace('~db=[^&]*&~', '', ME), 0, -1);
-               echo '<p id="breadcrumb"><a href="' . ($link != "" ? h($link) : ".") . '">' . (isset($_GET["server"]) ? h($_GET["server"]) : lang('Server')) . '</a> &raquo; ';
-               if (is_array($breadcrumb)) {
-                       if (DB != "") {
-                               echo '<a href="' . h(substr(ME, 0, -1)) . '">' . h(DB) . '</a> &raquo; ';
-                       }
-                       foreach ($breadcrumb as $key => $val) {
-                               $desc = (is_array($val) ? $val[1] : $val);
-                               if ($desc != "") {
-                                       echo '<a href="' . h(ME . "$key=") . urlencode(is_array($val) ? $val[0] : $val) . '">' . h($desc) . '</a> &raquo; ';
+               $server = (SERVER != "" ? h(SERVER) : lang('Server'));
+               if ($breadcrumb === false) {
+                       echo "$server\n";
+               } else {
+                       echo "<a href='" . ($link ? h($link) : ".") . "'>$server</a> &raquo; ";
+                       if (is_array($breadcrumb)) {
+                               if (DB != "") {
+                                       echo '<a href="' . h(substr(ME, 0, -1)) . '">' . h(DB) . '</a> &raquo; ';
+                               }
+                               foreach ($breadcrumb as $key => $val) {
+                                       $desc = (is_array($val) ? $val[1] : $val);
+                                       if ($desc != "") {
+                                               echo '<a href="' . h(ME . "$key=") . urlencode(is_array($val) ? $val[0] : $val) . '">' . h($desc) . '</a> &raquo; ';
+                                       }
                                }
                        }
+                       echo "$title\n";
                }
-               echo "$title\n";
        }
        echo "<h2>$title_all</h2>\n";
        restart_session();
@@ -45,10 +59,7 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
                echo "<div class='message'>" . implode("</div>\n<div class='message'>", $_SESSION["messages"]) . "</div>\n";
                $_SESSION["messages"] = array();
        }
-       if (!$_POST && !isset($_SESSION["passwords"])) { // used in auth
-               $_SESSION["passwords"] = array();
-       }
-       $databases = &$_SESSION["databases"][$_GET["server"]];
+       $databases = &get_session("databases");
        if (DB != "" && $databases && !in_array(DB, $databases, true)) {
                $databases = null;
        }
@@ -57,7 +68,11 @@ function page_header($title, $error = "", $breadcrumb = array(), $title2 = "") {
        }
 }
 
-function page_footer($missing = false) {
+/** Print HTML footer
+* @param string auth|db
+* @return null
+*/
+function page_footer($missing = "") {
        global $adminer;
        ?>
 </div>
@@ -67,4 +82,8 @@ function page_footer($missing = false) {
 <?php $adminer->navigation($missing); ?>
 </div>
 <?php
+       // don't wait for code after footer
+       session_write_close();
+       ob_flush();
+       flush();
 }
index 09019290b1543a894b88b61ef19cc775a8f1c54a..3a2260b713bfb13ad0da322f9e44ee45b4a844ed 100644 (file)
@@ -84,8 +84,8 @@ function select($result, $connection2 = null) {
 */
 function referencable_primary($self) {
        $return = array(); // table_name => field
-       foreach (table_status_referencable() as $table_name => $table) {
-               if ($table_name != $self) {
+       foreach (table_status() as $table_name => $table) {
+               if ($table_name != $self && fk_support($table)) {
                        foreach (fields($table_name) as $field) {
                                if ($field["primary"]) {
                                        if ($return[$table_name]) { // multi column primary key
@@ -142,16 +142,19 @@ function process_type($field, $collate = "COLLATE") {
 /** Create SQL string from field
 * @param array basic field information
 * @param array information about field type
-* @return string
+* @return array array("field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT")
 */
 function process_field($field, $type_field) {
        global $connection;
-       return idf_escape($field["field"]) . process_type($type_field)
-               . ($field["null"] ? " NULL" : " NOT NULL") // NULL for timestamp
-               . (!isset($field["default"]) ? "" : " DEFAULT " . ($field["type"] == "timestamp" && eregi("^CURRENT_TIMESTAMP$", $field["default"]) ? $field["default"] : $connection->quote($field["default"])))
-               . ($field["on_update"] ? " ON UPDATE $field[on_update]" : "")
-               . " COMMENT " . $connection->quote($field["comment"])
-       ;
+       return array(
+               idf_escape($field["field"]),
+               process_type($type_field),
+               ($field["null"] ? " NULL" : " NOT NULL"), // NULL for timestamp
+               (isset($field["default"]) ? " DEFAULT " . ($field["type"] == "timestamp" && eregi("^CURRENT_TIMESTAMP$", $field["default"]) ? $field["default"] : $connection->quote($field["default"])) : ""),
+               ($field["on_update"] ? " ON UPDATE $field[on_update]" : ""),
+               (support("comment") && $field["comment"] != "" ? " COMMENT " . $connection->quote($field["comment"]) : ""),
+               ($field["auto_increment"] ? auto_increment() : ""),
+       );
 }
 
 /** Get type class to use in CSS
@@ -179,12 +182,11 @@ function type_class($type) {
 * @param array returned by referencable_primary()
 * @return bool column comments used
 */
-function edit_fields($fields, $collations, $type = "TABLE", $allowed = 0, $foreign_keys = array()) {
+function edit_fields($fields, $collations, $type = "TABLE", $allowed = 0, $foreign_keys = array(), $comments = false) {
        global $inout;
-       $column_comments = false;
        foreach ($fields as $field) {
                if ($field["comment"] != "") {
-                       $column_comments = true;
+                       $comments = true;
                        break;
                }
        }
@@ -197,9 +199,9 @@ function edit_fields($fields, $collations, $type = "TABLE", $allowed = 0, $forei
 <td><?php echo lang('Options'); ?>
 <?php if ($type == "TABLE") { ?>
 <td>NULL
-<td><input type="radio" name="auto_increment_col" value=""><acronym title="<?php echo lang('Auto Increment'); ?>">A_I</acronym>
+<td><input type="radio" name="auto_increment_col" value=""><acronym title="<?php echo lang('Auto Increment'); ?>">AI</acronym>
 <td class="hidden"><?php echo lang('Default values'); ?>
-<td<?php echo ($column_comments ? "" : " class='hidden'"); ?>><?php echo lang('Comment'); ?>
+<?php echo (support("comment") ? "<td" . ($comments ? "" : " class='hidden'") . ">" . lang('Comment') : ""); ?>
 <?php } ?>
 <td><?php echo "<input type='image' name='add[0]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>"; ?><script type="text/javascript">row_count = <?php echo count($fields); ?>;</script>
 </thead>
@@ -209,27 +211,24 @@ function edit_fields($fields, $collations, $type = "TABLE", $allowed = 0, $forei
                $display = (isset($_POST["add"][$i-1]) || (isset($field["field"]) && !$_POST["drop_col"][$i]));
                ?>
 <tr<?php echo ($display ? "" : " style='display: none;'"); ?>>
-<?php
-if ($type == "PROCEDURE") {
-       echo "<td>" . html_select("fields[$i][inout]", $inout, $field["inout"]);
-}
-?>
+<?php echo ($type == "PROCEDURE" ? "<td>" . html_select("fields[$i][inout]", $inout, $field["inout"]) : ""); ?>
 <th><?php if ($display) { ?><input name="fields[<?php echo $i; ?>][field]" value="<?php echo h($field["field"]); ?>" onchange="<?php echo ($field["field"] != "" || count($fields) > 1 ? "" : "editingAddRow(this, $allowed); "); ?>editingNameChange(this);" maxlength="64"><?php } ?><input type="hidden" name="fields[<?php echo $i; ?>][orig]" value="<?php echo h($field[($_POST ? "orig" : "field")]); ?>">
 <?php edit_type("fields[$i]", $field, $collations, $foreign_keys); ?>
 <?php if ($type == "TABLE") { ?>
 <td><?php echo checkbox("fields[$i][null]", 1, $field["null"]); ?>
 <td><input type="radio" name="auto_increment_col" value="<?php echo $i; ?>"<?php if ($field["auto_increment"]) { ?> checked<?php } ?>>
 <td class="hidden"><?php echo checkbox("fields[$i][has_default]", 1, $field["has_default"]); ?><input name="fields[<?php echo $i; ?>][default]" value="<?php echo h($field["default"]); ?>" onchange="this.previousSibling.checked = true;">
-<td<?php echo ($column_comments ? "" : " class='hidden'"); ?>><input name="fields[<?php echo $i; ?>][comment]" value="<?php echo h($field["comment"]); ?>" maxlength="255">
+<?php echo (support("comment") ? "<td" . ($comments ? "" : " class='hidden'") . "><input name='fields[$i][comment]' value='" . h($field["comment"]) . "' maxlength='255'>" : ""); ?>
 <?php } ?>
 <?php
+               //! hide operations not supported by the driver - column change, adding column not at the end, drop column, ...
                echo "<td><input type='image' name='add[$i]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "' onclick='return !editingAddRow(this, $allowed, 1);'>";
                echo "&nbsp;<input type='image' name='drop_col[$i]' src='../adminer/static/cross.gif' alt='x' title='" . lang('Remove') . "' onclick='return !editingRemoveRow(this);'>";
                echo "&nbsp;<input type='image' name='up[$i]' src='../adminer/static/up.gif' alt='^' title='" . lang('Move up') . "'>";
                echo "&nbsp;<input type='image' name='down[$i]' src='../adminer/static/down.gif' alt='v' title='" . lang('Move down') . "'>";
                echo "\n";
        }
-       return $column_comments;
+       return $comments;
 }
 
 /** Move fields up and down or add field
@@ -278,7 +277,7 @@ function process_fields(&$fields) {
 * @return string
 */
 function normalize_enum($match) {
-       return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($match[0]{0} . $match[0]{0}, $match[0]{0}, substr($match[0], 1, -1))), '\\')) . "'";
+       return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($match[0][0] . $match[0][0], $match[0][0], substr($match[0], 1, -1))), '\\')) . "'";
 }
 
 /** Get information about stored routine
@@ -291,7 +290,7 @@ function routine($name, $type) {
        $aliases = array("bit" => "tinyint", "bool" => "tinyint", "boolean" => "tinyint", "integer" => "int", "double precision" => "float", "real" => "float", "dec" => "decimal", "numeric" => "decimal", "fixed" => "decimal", "national char" => "char", "national varchar" => "varchar");
        $type_pattern = "(" . implode("|", array_keys($types + $aliases)) . ")(?:\\s*\\(((?:[^'\")]*|$enum_length)+)\\))?\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s]+)['\"]?)?";
        $pattern = "\\s*(" . ($type == "FUNCTION" ? "" : implode("|", $inout)) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
-       $create = $connection->result($connection->query("SHOW CREATE $type " . idf_escape($name)), 2);
+       $create = $connection->result("SHOW CREATE $type " . idf_escape($name), 2);
        preg_match("~\\(((?:$pattern\\s*,?)*)\\)" . ($type == "FUNCTION" ? "\\s*RETURNS\\s+$type_pattern" : "") . "\\s*(.*)~is", $create, $match);
        $fields = array();
        preg_match_all("~$pattern\\s*,?~is", $match[1], $matches, PREG_SET_ORDER);
index 1847b8fd2c9d6dd578d108fc15d68c28ed4839db..e0b4797dd35d15c381007562a7f252fcf71f7cfb 100644 (file)
@@ -11,7 +11,7 @@ function tar_file($filename, $contents) {
 
 function dump_triggers($table, $style) {
        global $connection;
-       if ($_POST["format"] == "sql" && $style && $connection->server_info >= 5) {
+       if ($_POST["format"] == "sql" && $style && support("trigger")) {
                $result = $connection->query("SHOW TRIGGERS LIKE " . $connection->quote(addcslashes($table, "%_")));
                if ($result->num_rows) {
                        $s = "\nDELIMITER ;;\n";
@@ -26,18 +26,17 @@ function dump_triggers($table, $style) {
 
 function dump_table($table, $style, $is_view = false) {
        global $connection;
-       if ($_POST["format"] == "csv") {
+       if ($_POST["format"] != "sql") {
                echo "\xef\xbb\xbf"; // UTF-8 byte order mark
                if ($style) {
                        dump_csv(array_keys(fields($table)));
                }
        } elseif ($style) {
-               $result = $connection->query("SHOW CREATE TABLE " . idf_escape($table));
-               if ($result) {
+               $create = create_sql($table);
+               if ($create) {
                        if ($style == "DROP+CREATE") {
                                echo "DROP " . ($is_view ? "VIEW" : "TABLE") . " IF EXISTS " . idf_escape($table) . ";\n";
                        }
-                       $create = $connection->result($result, 1);
                        echo ($style != "CREATE+ALTER" ? $create : ($is_view ? substr_replace($create, " OR REPLACE", 6, 0) : substr_replace($create, " IF NOT EXISTS", 12, 0))) . ";\n\n";
                }
                if ($style == "CREATE+ALTER" && !$is_view) {
@@ -116,10 +115,10 @@ DROP PROCEDURE adminer_alter;
 }
 
 function dump_data($table, $style, $select = "") {
-       global $connection;
-       $max_packet = 1048576; // default, minimum is 1024
+       global $connection, $driver;
+       $max_packet = ($driver == "sqlite" ? 0 : 1048576); // default, minimum is 1024
        if ($style) {
-               if ($_POST["format"] != "csv" && $style == "TRUNCATE+INSERT") {
+               if ($_POST["format"] == "sql" && $style == "TRUNCATE+INSERT") {
                        echo "TRUNCATE " . idf_escape($table) . ";\n";
                }
                $fields = fields($table);
@@ -128,7 +127,7 @@ function dump_data($table, $style, $select = "") {
                        $insert = "";
                        $buffer = "";
                        while ($row = $result->fetch_assoc()) {
-                               if ($_POST["format"] == "csv") {
+                               if ($_POST["format"] != "sql") {
                                        dump_csv($row);
                                } else {
                                        if (!$insert) {
@@ -145,7 +144,7 @@ function dump_data($table, $style, $select = "") {
                                                }
                                                echo "$insert ($s) ON DUPLICATE KEY UPDATE " . implode(", ", $set) . ";\n";
                                        } else {
-                                               $s = "\n($s)";
+                                               $s = ($max_packet ? "\n" : " ") . "($s)";
                                                if (!$buffer) {
                                                        $buffer = $insert . $s;
                                                } elseif (strlen($buffer) + 2 + strlen($s) < $max_packet) { // 2 - separator and terminator length
@@ -158,7 +157,7 @@ function dump_data($table, $style, $select = "") {
                                        }
                                }
                        }
-                       if ($_POST["format"] != "csv" && $style != "INSERT+UPDATE" && $buffer) {
+                       if ($_POST["format"] == "sql" && $style != "INSERT+UPDATE" && $buffer) {
                                $buffer .= ";\n";
                                echo $buffer;
                        }
index a8f12cdfc52a92c11c0ce289fcef34ae11a56d9a..0118cfe405cf24e6280ef18897d6af0e7e6c15d1 100644 (file)
@@ -8,20 +8,12 @@ function connection() {
        return $connection;
 }
 
-/** Escape database identifier
-* @param string
-* @return string
-*/
-function idf_escape($idf) {
-       return "`" . str_replace("`", "``", $idf) . "`";
-}
-
 /** Unescape database identifier
 * @param string text inside ``
 * @return string
 */
 function idf_unescape($idf) {
-       return str_replace("``", "`", $idf);
+       return str_replace($idf[0] . $idf[0], $idf[0], substr($idf, 1, -1));
 }
 
 /** Escape string to use inside ''
@@ -60,6 +52,14 @@ function nbsp($string) {
        return (trim($string) != "" ? h($string) : "&nbsp;");
 }
 
+/** Convert \n to <br>
+* @param string
+* @return string
+*/
+function nl_br($string) {
+       return str_replace("\n", "<br>", $string); // nl2br() uses XHTML before PHP 5.3
+}
+
 /** Generate HTML checkbox
 * @param string
 * @param string
@@ -71,7 +71,7 @@ function nbsp($string) {
 function checkbox($name, $value, $checked, $label = "", $onclick = "") {
        static $id = 0;
        $id++;
-       $return = "<input type='checkbox' name='$name' value='" . h($value) . "'" . ($checked ? " checked" : "") . ($onclick ? " onclick=\"$onclick\"" : "") . " id='checkbox-$id'>";
+       $return = "<input type='checkbox'" . ($name ? " name='$name' value='" . h($value) . "'" : "") . ($checked ? " checked" : "") . ($onclick ? " onclick=\"$onclick\"" : "") . " id='checkbox-$id'>";
        return ($label != "" ? "<label for='checkbox-$id'>$return" . h($label) . "</label>" : $return);
 }
 
@@ -115,6 +115,15 @@ function html_select($name, $options, $value = "", $onchange = true) {
        return $return;
 }
 
+/** Get INI boolean value
+* @param string
+* @return bool
+*/
+function ini_bool($ini) {
+       $val = ini_get($ini);
+       return (eregi('^(on|true|yes)$', $val) || (int) $val); // boolean values set by php_value are strings
+}
+
 /** Get list of values from database
 * @param string
 * @param mixed
@@ -132,6 +141,24 @@ function get_vals($query, $column = 0) {
        return $return;
 }
 
+/** Get keys from first column and values from second
+* @param string
+* @param Min_DB
+* @return array
+*/
+function get_key_vals($query, $connection2 = null) {
+       global $connection;
+       if (!is_object($connection2)) {
+               $connection2 = $connection;
+       }
+       $return = array();
+       $result = $connection2->query($query);
+       while ($row = $result->fetch_row()) {
+               $return[$row[0]] = $row[1];
+       }
+       return $return;
+}
+
 /** Find unique identifier of a row
 * @param array
 * @param array result of indexes()
@@ -200,7 +227,14 @@ function where_link($i, $column, $value, $operator = "=") {
 * @return bool
 */
 function cookie($name, $value) {
-       $params = array($name, $value, time() + 2592000, preg_replace('~\\?.*~', '', $_SERVER["REQUEST_URI"]), "", $_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off")); // 2592000 = 30 * 24 * 60 * 60
+       $params = array(
+               $name,
+               (ereg("\n", $value) ? "" : $value), // HTTP Response Splitting protection in PHP < 5.1.2
+               time() + 2592000, // 2592000 - 30 days
+               preg_replace('~\\?.*~', '', $_SERVER["REQUEST_URI"]),
+               "",
+               $_SERVER["HTTPS"] && strcasecmp($_SERVER["HTTPS"], "off")
+       );
        if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
                $params[] = true; // HttpOnly
        }
@@ -211,11 +245,28 @@ function cookie($name, $value) {
 * @return null
 */
 function restart_session() {
-       if (!ini_get("session.use_cookies")) {
+       if (!ini_bool("session.use_cookies")) {
                session_start();
        }
 }
 
+/** Get session variable for current server
+* @param string
+* @return mixed
+*/
+function &get_session($key) {
+       return $_SESSION[$key][DRIVER][SERVER][$_GET["username"]];
+}
+
+/** Set session variable for current server
+* @param string
+* @param mixed
+* @return mixed
+*/
+function set_session($key, $val) {
+       $_SESSION[$key][DRIVER][SERVER][$_GET["username"]] = $val; // used also in auth.inc.php
+}
+
 /** Send Location header and exit
 * @param string null to only set a message
 * @param string
@@ -385,6 +436,15 @@ function hidden_fields($process, $ignore = array()) {
        }
 }
 
+/** Print hidden fields for GET forms
+* @return null
+*/
+function hidden_fields_get() {
+       echo (SID && !$_COOKIE ? '<input type="hidden" name="' . session_name() . '" value="' . h(session_id()) . '">' : '');
+       echo (SERVER !== null ? '<input type="hidden" name="' . DRIVER . '" value="' . h(SERVER) . '">' : "");
+       echo '<input type="hidden" name="username" value="' . h($_GET["username"]) . '">';
+}
+
 /** Find out foreign keys for each column
 * @param string
 * @return array array($col => array())
@@ -399,6 +459,22 @@ function column_foreign_keys($table) {
        return $return;
 }
 
+/** Print enum input field
+* @param string "radio"|"checkbox"
+* @param string
+* @param array
+* @param mixed int|string|array
+* @return null
+*/
+function enum_input($type, $name, $field, $value) {
+       preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
+       foreach ($matches[1] as $i => $val) {
+               $val = stripcslashes(str_replace("''", "'", $val));
+               $checked = (is_int($value) ? $value == $i+1 : (is_array($value) ? in_array($i+1, $value) : $value === $val));
+               echo " <label><input type='$type' name='$name' value='" . ($i+1) . "'" . ($checked ? ' checked' : '') . '>' . h($val) . '</label>';
+       }
+}
+
 /** Print edit input field
 * @param array one field from fields()
 * @param mixed
@@ -406,19 +482,14 @@ function column_foreign_keys($table) {
 * @return null
 */
 function input($field, $value, $function) {
-       global $types, $adminer;
+       global $types, $adminer, $driver;
        $name = h(bracket_escape($field["field"]));
        echo "<td class='function'>";
        $functions = (isset($_GET["select"]) ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field);
        if ($field["type"] == "enum") {
                echo nbsp($functions[""]) . "<td>" . ($functions["orig"] ? "<label><input type='radio' name='fields[$name]' value='-1' checked><em>$functions[orig]</em></label> " : "");
                echo $adminer->editInput($_GET["edit"], $field, " name='fields[$name]'", $value);
-               preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
-               foreach ($matches[1] as $i => $val) {
-                       $val = stripcslashes(str_replace("''", "'", $val));
-                       $checked = (is_int($value) ? $value == $i+1 : $value === $val);
-                       echo " <label><input type='radio' name='fields[$name]' value='" . ($i+1) . "'" . ($checked ? ' checked' : '') . '>' . h($val) . '</label>';
-               }
+               enum_input("radio", "fields[$name]", $field, $value);
        } else {
                $first = 0;
                foreach ($functions as $key => $val) {
@@ -440,10 +511,10 @@ function input($field, $value, $function) {
                                $checked = (is_int($value) ? ($value >> $i) & 1 : in_array($val, explode(",", $value), true));
                                echo " <label><input type='checkbox' name='fields[$name][$i]' value='" . (1 << $i) . "'" . ($checked ? ' checked' : '') . "$onchange>" . h($val) . '</label>';
                        }
-               } elseif (ereg('binary|blob', $field["type"]) && ini_get("file_uploads")) {
+               } elseif (ereg('binary|blob|bytea', $field["type"]) && ini_bool("file_uploads")) {
                        echo "<input type='file' name='fields-$name'$onchange>";
                } elseif (ereg('text|blob', $field["type"])) {
-                       echo "<textarea cols='50' rows='12'$attrs>" . h($value) . '</textarea>';
+                       echo "<textarea cols='50' rows='" . ($driver != "sqlite" || ereg("\n", $value) ? 12 : 1) . "'$attrs>" . h($value) . '</textarea>';
                } else {
                        // int(3) is only a display hint
                        $maxlength = (!ereg('int', $field["type"]) && preg_match('~^([0-9]+)(,([0-9]+))?$~', $field["length"], $match) ? ($match[1] + ($match[3] ? 1 : 0) + ($match[2] && !$field["unsigned"] ? 1 : 0)) : ($types[$field["type"]] ? $types[$field["type"]] + ($field["unsigned"] ? 0 : 1) : 0));
@@ -461,23 +532,35 @@ function process_input($field) {
        $idf = bracket_escape($field["field"]);
        $function = $_POST["function"][$idf];
        $value = $_POST["fields"][$idf];
-       if ($field["type"] == "enum" ? $value == -1 : $function == "orig") {
+       if ($field["type"] == "enum") {
+               if ($value == -1) {
+                       return false;
+               }
+               if ($value == "") {
+                       return "NULL";
+               }
+               return intval($value);
+       }
+       if ($field["auto_increment"] && $value == "") {
+               return null;
+       }
+       if ($function == "orig") {
                return false;
-       } elseif ($field["type"] == "enum" || $field["auto_increment"] ? $value == "" : $function == "NULL") {
+       }
+       if ($function == "NULL") {
                return "NULL";
-       } elseif ($field["type"] == "enum") {
-               return intval($value);
-       } elseif ($field["type"] == "set") {
+       }
+       if ($field["type"] == "set") {
                return array_sum((array) $value);
-       } elseif (ereg('binary|blob', $field["type"]) && ini_get("file_uploads")) {
+       }
+       if (ereg('binary|blob|bytea', $field["type"]) && ini_bool("file_uploads")) {
                $file = get_file("fields-$idf");
                if (!is_string($file)) {
                        return false; //! report errors
                }
                return $connection->quote($file);
-       } else {
-               return $adminer->processInput($field, $value, $function);
        }
+       return $adminer->processInput($field, $value, $function);
 }
 
 /** Print results of search in all tables
@@ -491,7 +574,7 @@ function search_tables() {
        foreach (table_status() as $table => $table_status) {
                $name = $adminer->tableName($table_status);
                if (isset($table_status["Engine"]) && $name != "" && (!$_POST["tables"] || in_array($table, $_POST["tables"]))) {
-                       $result = $connection->query("SELECT 1 FROM " . idf_escape($table) . " WHERE " . implode(" AND ", $adminer->selectSearchProcess(fields($table), array())) . " LIMIT 1");
+                       $result = $connection->query("SELECT" . limit("1 FROM " . idf_escape($table) . " WHERE " . implode(" AND ", $adminer->selectSearchProcess(fields($table), array())), 1));
                        if ($result->num_rows) {
                                if (!$found) {
                                        echo "<ul>\n";
@@ -510,11 +593,11 @@ function search_tables() {
 */
 function dump_csv($row) {
        foreach ($row as $key => $val) {
-               if (preg_match("~[\"\n,]~", $val) || $val === "") {
+               if (preg_match("~[\"\n,;]~", $val) || $val === "") {
                        $row[$key] = '"' . str_replace('"', '""', $val) . '"';
                }
        }
-       echo implode(",", $row) . "\n";
+       echo implode(($_POST["format"] == "csv;" ? ";" : ","), $row) . "\n";
 }
 
 /** Apply SQL function
@@ -523,7 +606,7 @@ function dump_csv($row) {
 * @return string
 */
 function apply_sql_function($function, $column) {
-       return ($function ? ($function == "count distinct" ? "COUNT(DISTINCT " : strtoupper("$function(")) . "$column)" : $column);
+       return ($function ? ($function == "unixepoch" ? "DATETIME($column, '$function')" : ($function == "count distinct" ? "COUNT(DISTINCT " : strtoupper("$function(")) . "$column)") : $column);
 }
 
 /** Check whether the string is e-mail address
diff --git a/adminer/include/mysql.inc.php b/adminer/include/mysql.inc.php
deleted file mode 100644 (file)
index 3649992..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-<?php
-// MySQLi supports everything, MySQL doesn't support multiple result sets, PDO_MySQL doesn't support orgtable
-if (extension_loaded("mysqli")) {
-       class Min_DB extends MySQLi {
-               var $extension = "MySQLi";
-               
-               function Min_DB() {
-                       parent::init();
-               }
-               
-               function connect($server, $username, $password) {
-                       list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket
-                       return @$this->real_connect(
-                               ($server != "" ? $host : ini_get("mysqli.default_host")),
-                               ("$server$username" != "" ? $username : ini_get("mysqli.default_user")),
-                               ("$server$username$password" != "" ? $password : ini_get("mysqli.default_pw")),
-                               null,
-                               (is_numeric($port) ? $port : ini_get("mysqli.default_port")),
-                               (!is_numeric($port) ? $port : null)
-                       );
-               }
-               
-               function result($result, $field = 0) {
-                       if (!$result) {
-                               return false;
-                       }
-                       $row = $result->fetch_array();
-                       return $row[$field];
-               }
-               
-               function quote($string) {
-                       return "'" . $this->escape_string($string) . "'";
-               }
-       }
-       
-} elseif (extension_loaded("mysql")) {
-       class Min_DB {
-               var $extension = "MySQL", $_link, $_result, $server_info, $affected_rows, $error;
-               
-               function connect($server, $username, $password) {
-                       $this->_link = @mysql_connect(
-                               ($server != "" ? $server : ini_get("mysql.default_host")),
-                               ("$server$username" != "" ? $username : ini_get("mysql.default_user")),
-                               ("$server$username$password" != "" ? $password : ini_get("mysql.default_password")),
-                               true,
-                               131072 // CLIENT_MULTI_RESULTS for CALL
-                       );
-                       if ($this->_link) {
-                               $this->server_info = mysql_get_server_info($this->_link);
-                       } else {
-                               $this->error = mysql_error();
-                       }
-                       return (bool) $this->_link;
-               }
-               
-               function quote($string) {
-                       return "'" . mysql_real_escape_string($string, $this->_link) . "'";
-               }
-               
-               function select_db($database) {
-                       return mysql_select_db($database, $this->_link);
-               }
-               
-               function query($query, $unbuffered = false) {
-                       $result = @($unbuffered ? mysql_unbuffered_query($query, $this->_link) : mysql_query($query, $this->_link)); // @ - mute mysql.trace_mode
-                       if (!$result) {
-                               $this->error = mysql_error($this->_link);
-                               return false;
-                       }
-                       if ($result === true) {
-                               $this->affected_rows = mysql_affected_rows($this->_link);
-                               $this->info = mysql_info($this->_link);
-                               return true;
-                       }
-                       return new Min_Result($result);
-               }
-               
-               function multi_query($query) {
-                       return $this->_result = $this->query($query);
-               }
-               
-               function store_result() {
-                       return $this->_result;
-               }
-               
-               function next_result() {
-                       // MySQL extension doesn't support multiple results
-                       return false;
-               }
-               
-               function result($result, $field = 0) {
-                       if (!$result) {
-                               return false;
-                       }
-                       return mysql_result($result->_result, 0, $field);
-               }
-       }
-       
-       class Min_Result {
-               var $_result, $_offset = 0, $num_rows;
-               
-               function Min_Result($result) {
-                       $this->_result = $result;
-                       $this->num_rows = mysql_num_rows($result);
-               }
-               
-               function fetch_assoc() {
-                       return mysql_fetch_assoc($this->_result);
-               }
-               
-               function fetch_row() {
-                       return mysql_fetch_row($this->_result);
-               }
-               
-               function fetch_field() {
-                       $row = mysql_fetch_field($this->_result, $this->_offset++);
-                       $row->orgtable = $row->table;
-                       $row->orgname = $row->name;
-                       $row->charsetnr = ($row->blob ? 63 : 0);
-                       return $row;
-               }
-               
-               function __destruct() {
-                       mysql_free_result($this->_result); //! is not called in PHP 4 which is a problem with mysql.trace_mode
-               }
-       }
-       
-} elseif (extension_loaded("pdo_mysql")) {
-       class Min_DB extends Min_PDO {
-               var $extension = "PDO_MySQL";
-               
-               function connect($server, $username, $password) {
-                       $this->dsn("mysql:host=" . str_replace(":", ";unix_socket=", preg_replace('~:([0-9])~', ';port=\\1', $server)), $username, $password);
-                       $this->server_info = $this->result($this->query("SELECT VERSION()"));
-                       return true;
-               }
-               
-               function query($query, $unbuffered = false) {
-                       $this->setAttribute(1000, !$unbuffered); // 1000 - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
-                       return parent::query($query, $unbuffered);
-               }
-       }
-       
-} else {
-       page_header(lang('No MySQL extension'), lang('None of the supported PHP extensions (%s) are available.', 'MySQLi, MySQL, PDO_MySQL'), null);
-       page_footer("auth");
-       exit;
-}
-
-/** Connect to the database
-* @return mixed Min_DB or string for error
-*/
-function connect() {
-       global $adminer;
-       $connection = new Min_DB;
-       $credentials = $adminer->credentials();
-       if ($connection->connect($credentials[0], $credentials[1], $credentials[2])) {
-               $connection->query("SET SQL_QUOTE_SHOW_CREATE=1");
-               $connection->query("SET NAMES utf8");
-               return $connection;
-       }
-       return $connection->error;
-}
-
-/** Get cached list of databases
-* @param bool
-* @return array
-*/
-function get_databases($flush = true) {
-       // SHOW DATABASES can take a very long time so it is cached
-       $return = &$_SESSION["databases"][$_GET["server"]];
-       if (!isset($return)) {
-               restart_session();
-               $return = get_vals("SHOW DATABASES");
-               if ($flush) {
-                       ob_flush();
-                       flush();
-               }
-       }
-       return $return;
-}
-
-/** Get database collation
-* @param string
-* @param array result of collations()
-* @return array
-*/
-function db_collation($db, $collations) {
-       global $connection;
-       $return = null;
-       $result = $connection->query("SHOW CREATE DATABASE " . idf_escape($db));
-       if ($result) {
-               $create = $connection->result($result, 1);
-               if (preg_match('~ COLLATE ([^ ]+)~', $create, $match)) {
-                       $return = $match[1];
-               } elseif (preg_match('~ CHARACTER SET ([^ ]+)~', $create, $match)) {
-                       // default collation
-                       $return = $collations[$match[1]][0];
-               }
-       }
-       return $return;
-}
-
-/**Get supported engines 
-* @return array
-*/
-function engines() {
-       global $connection;
-       $return = array();
-       $result = $connection->query("SHOW ENGINES");
-       while ($row = $result->fetch_assoc()) {
-               if (ereg("YES|DEFAULT", $row["Support"])) {
-                       $return[] = $row["Engine"];
-               }
-       }
-       return $return;
-}
-
-/** Get tables list
-* @return array
-*/
-function tables_list() {
-       return get_vals("SHOW TABLES");
-}
-
-/** Get table status
-* @param string
-* @return array
-*/
-function table_status($name = "") {
-       global $connection;
-       $return = array();
-       $result = $connection->query("SHOW TABLE STATUS" . ($name != "" ? " LIKE " . $connection->quote(addcslashes($name, "%_")) : ""));
-       while ($row = $result->fetch_assoc()) {
-               if ($row["Engine"] == "InnoDB") {
-                       // ignore internal comment, unnecessary since MySQL 5.1.21
-                       $row["Comment"] = preg_replace('~(?:(.+); )?InnoDB free: .*~', '\\1', $row["Comment"]);
-               }
-               if ($name != "") {
-                       return $row;
-               }
-               $return[$row["Name"]] = $row;
-       }
-       return $return;
-}
-
-/** Get status of referencable tables
-* @return array
-*/
-function table_status_referencable() {
-       $return = array();
-       foreach (table_status() as $name => $row) {
-               if ($row["Engine"] == "InnoDB") {
-                       $return[$name] = $row;
-               }
-       }
-       return $return;
-}
-
-/** Get information about fields
-* @param string
-* @return array array($name => array("field" => , "full_type" => , "type" => , "length" => , "unsigned" => , "default" => , "null" => , "auto_increment" => , "on_update" => , "collation" => , "privileges" => , "comment" => , "primary" => ))
-*/
-function fields($table) {
-       global $connection;
-       $return = array();
-       $result = $connection->query("SHOW FULL COLUMNS FROM " . idf_escape($table));
-       if ($result) {
-               while ($row = $result->fetch_assoc()) {
-                       preg_match('~^([^( ]+)(?:\\((.+)\\))?( unsigned)?( zerofill)?$~', $row["Type"], $match);
-                       $return[$row["Field"]] = array(
-                               "field" => $row["Field"],
-                               "full_type" => $row["Type"],
-                               "type" => $match[1],
-                               "length" => $match[2],
-                               "unsigned" => ltrim($match[3] . $match[4]),
-                               "default" => ($row["Default"] != "" || ereg("char", $match[1]) ? $row["Default"] : null),
-                               "null" => ($row["Null"] == "YES"),
-                               "auto_increment" => ($row["Extra"] == "auto_increment"),
-                               "on_update" => (eregi('^on update (.+)', $row["Extra"], $match) ? $match[1] : ""), //! available since MySQL 5.1.23
-                               "collation" => $row["Collation"],
-                               "privileges" => array_flip(explode(",", $row["Privileges"])),
-                               "comment" => $row["Comment"],
-                               "primary" => ($row["Key"] == "PRI"),
-                       );
-               }
-       }
-       return $return;
-}
-
-/** Get table indexes
-* @param string
-* @param string Min_DB to use
-* @return array array($key_name => array("type" => , "columns" => array(), "lengths" => array()))
-*/
-function indexes($table, $connection2 = null) {
-       global $connection;
-       if (!is_object($connection2)) { // use the main connection if the separate connection is unavailable
-               $connection2 = $connection;
-       }
-       $return = array();
-       $result = $connection2->query("SHOW INDEX FROM " . idf_escape($table));
-       if ($result) {
-               while ($row = $result->fetch_assoc()) {
-                       $return[$row["Key_name"]]["type"] = ($row["Key_name"] == "PRIMARY" ? "PRIMARY" : ($row["Index_type"] == "FULLTEXT" ? "FULLTEXT" : ($row["Non_unique"] ? "INDEX" : "UNIQUE")));
-                       $return[$row["Key_name"]]["columns"][$row["Seq_in_index"]] = $row["Column_name"];
-                       $return[$row["Key_name"]]["lengths"][$row["Seq_in_index"]] = $row["Sub_part"];
-               }
-       }
-       return $return;
-}
-
-/** Get foreign keys in table
-* @param string
-* @return array array($name => array("db" => , "table" => , "source" => array(), "target" => array(), "on_delete" => , "on_update" => ))
-*/
-function foreign_keys($table) {
-       global $connection, $on_actions;
-       static $pattern = '(?:[^`]|``)+';
-       $return = array();
-       $result = $connection->query("SHOW CREATE TABLE " . idf_escape($table));
-       if ($result) {
-               $create_table = $connection->result($result, 1);
-               preg_match_all("~CONSTRAINT `($pattern)` FOREIGN KEY \\(((?:`$pattern`,? ?)+)\\) REFERENCES `($pattern)`(?:\\.`($pattern)`)? \\(((?:`$pattern`,? ?)+)\\)(?: ON DELETE (" . implode("|", $on_actions) . "))?(?: ON UPDATE (" . implode("|", $on_actions) . "))?~", $create_table, $matches, PREG_SET_ORDER);
-               foreach ($matches as $match) {
-                       preg_match_all("~`($pattern)`~", $match[2], $source);
-                       preg_match_all("~`($pattern)`~", $match[5], $target);
-                       $return[$match[1]] = array(
-                               "db" => idf_unescape($match[4] != "" ? $match[3] : $match[4]),
-                               "table" => idf_unescape($match[4] != "" ? $match[4] : $match[3]),
-                               "source" => array_map('idf_unescape', $source[1]),
-                               "target" => array_map('idf_unescape', $target[1]),
-                               "on_delete" => $match[6],
-                               "on_update" => $match[7],
-                       );
-               }
-       }
-       return $return;
-}
-
-/** Get view SELECT
-* @param string
-* @return array array("select" => )
-*/
-function view($name) {
-       global $connection;
-       return array("select" => preg_replace('~^(?:[^`]|`[^`]*`)* AS ~U', '', $connection->result($connection->query("SHOW CREATE VIEW " . idf_escape($name)), 1)));
-}
-
-/** Get sorted grouped list of collations
-* @return array
-*/
-function collations() {
-       global $connection;
-       $return = array();
-       $result = $connection->query("SHOW COLLATION");
-       while ($row = $result->fetch_assoc()) {
-               $return[$row["Charset"]][] = $row["Collation"];
-       }
-       ksort($return);
-       foreach ($return as $key => $val) {
-               sort($return[$key]);
-       }
-       return $return;
-}
-
-/** Find out if database is information_schema
-* @param string
-* @return bool
-*/
-function information_schema($db) {
-       global $connection;
-       return ($connection->server_info >= 5 && $db == "information_schema");
-}
-
-/** Get escaped error message
-* @return string
-*/
-function error() {
-       global $connection;
-       return h(preg_replace('~^You have an error.*syntax to use~U', "Syntax error", $connection->error));
-}
-
-/** Return expression for binary comparison
-* @param string
-* @return string
-*/
-function exact_value($val) {
-       global $connection;
-       return "BINARY " . $connection->quote($val);
-}
-
-// value means maximum unsigned length
-$types = array();
-$structured_types = array();
-foreach (array(
-       lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "mediumint" => 8, "int" => 10, "bigint" => 20, "decimal" => 66, "float" => 12, "double" => 21),
-       lang('Date and time') => array("date" => 10, "datetime" => 19, "timestamp" => 19, "time" => 10, "year" => 4),
-       lang('Strings') => array("char" => 255, "varchar" => 65535, "tinytext" => 255, "text" => 65535, "mediumtext" => 16777215, "longtext" => 4294967295),
-       lang('Binary') => array("binary" => 255, "varbinary" => 65535, "tinyblob" => 255, "blob" => 65535, "mediumblob" => 16777215, "longblob" => 4294967295),
-       lang('Lists') => array("enum" => 65535, "set" => 64),
-) as $key => $val) {
-       $types += $val;
-       $structured_types[$key] = array_keys($val);
-}
-$unsigned = array("unsigned", "zerofill", "unsigned zerofill");
index a8d949ec7a9f0a0698d491b8162897296ce56002..359e5a8a7751c51043ab0cd160b229848c44bc99 100644 (file)
@@ -1,23 +1,21 @@
 <?php
 // PDO can be used in several database drivers
 if (extension_loaded('pdo')) {
-       class Min_PDO extends PDO {
+       /*abstract */class Min_PDO extends PDO {
                var $_result, $server_info, $affected_rows, $error;
                
                function __construct() {
                }
                
-               function dsn($dsn, $username, $password) {
-                       set_exception_handler('auth_error'); // try/catch is not compatible with PHP 4
+               function dsn($dsn, $username, $password, $exception_handler = 'auth_error') {
+                       set_exception_handler($exception_handler); // try/catch is not compatible with PHP 4
                        parent::__construct($dsn, $username, $password);
                        restore_exception_handler();
-                       $this->setAttribute(13, array('Min_PDOStatement')); // PDO::ATTR_STATEMENT_CLASS
+                       $this->setAttribute(13, array('Min_PDOStatement')); // 13 - PDO::ATTR_STATEMENT_CLASS
+                       $this->server_info = $this->getAttribute(4); // 4 - PDO::ATTR_SERVER_VERSION
                }
                
-               function select_db($database) {
-                       // database selection is separated from the connection so dbname in DSN can't be used
-                       return $this->query("USE " . idf_escape($database));
-               }
+               /*abstract function select_db($database);*/
                
                function query($query, $unbuffered = false) {
                        $result = parent::query($query);
@@ -50,7 +48,8 @@ if (extension_loaded('pdo')) {
                        return $this->_result->nextRowset();
                }
                
-               function result($result, $field = 0) {
+               function result($query, $field = 0) {
+                       $result = $this->query($query);
                        if (!$result) {
                                return false;
                        }
@@ -79,3 +78,6 @@ if (extension_loaded('pdo')) {
                }
        }
 }
+
+$possible_drivers = array();
+$drivers = array();
index 8464b96698a12426c493b2f06469daf10806040a..ce425176b6db550e92026f94f3b01a4858f4b236 100644 (file)
@@ -1,2 +1,2 @@
 <?php
-$VERSION = "2.3.2";
+$VERSION = "3.0.0-dev";
index 72563c25f5f84ce7b09fd5b4b5a2649c4ff2e23c..2c9f5f51fc8231bb2884e4c9e31206479b26d4cc 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Adminer - Compact MySQL management
+/** Adminer - Compact database management
 * @link http://www.adminer.org/
 * @author Jakub Vrana, http://php.vrana.cz/
 * @copyright 2007 Jakub Vrana
index 239379d6f3cf8a35595b3b8d3ceb8a8cb72f2bc2..c7767e5d29e446b9eed4bd89bcec9f271ac7bdce 100644 (file)
@@ -1,6 +1,10 @@
 <?php
 $TABLE = $_GET["indexes"];
-$index_types = array("PRIMARY", "UNIQUE", "INDEX", "FULLTEXT");
+$index_types = array("PRIMARY", "UNIQUE", "INDEX");
+$table_status = table_status($TABLE);
+if (ereg("MyISAM|Maria", $table_status["Engine"])) {
+       $index_types[] = "FULLTEXT";
+}
 $indexes = indexes($TABLE);
 if ($_POST && !$error && !$_POST["add"]) {
        $alter = array();
@@ -14,32 +18,32 @@ if ($_POST && !$error && !$_POST["add"]) {
                                if ($column != "") {
                                        $length = $index["lengths"][$key];
                                        $set[] = idf_escape($column) . ($length ? "(" . intval($length) . ")" : "");
-                                       $columns[count($columns) + 1] = $column;
-                                       $lengths[count($lengths) + 1] = ($length ? $length : null);
+                                       $columns[] = $column;
+                                       $lengths[] = ($length ? $length : null);
                                }
                        }
                        if ($columns) {
                                foreach ($indexes as $name => $existing) {
                                        ksort($existing["columns"]);
                                        ksort($existing["lengths"]);
-                                       if ($index["type"] == $existing["type"] && $existing["columns"] === $columns && $existing["lengths"] === $lengths) {
+                                       if ($index["type"] == $existing["type"] && array_values($existing["columns"]) === $columns && (!$existing["lengths"] || array_values($existing["lengths"]) === $lengths)) {
                                                // skip existing index
                                                unset($indexes[$name]);
                                                continue 2;
                                        }
                                }
-                               $alter[] = "\nADD $index[type]" . ($index["type"] == "PRIMARY" ? " KEY" : "") . " (" . implode(", ", $set) . ")";
+                               $alter[] = array($index["type"], "(" . implode(", ", $set) . ")");
                        }
                }
        }
        // drop removed indexes
        foreach ($indexes as $name => $existing) {
-               $alter[] = "\nDROP INDEX " . idf_escape($name);
+               $alter[] = array($existing["type"], idf_escape($name), "DROP");
        }
        if (!$alter) {
                redirect(ME . "table=" . urlencode($TABLE));
        }
-       query_redirect("ALTER TABLE " . idf_escape($TABLE) . implode(",", $alter), ME . "table=" . urlencode($TABLE), lang('Indexes have been altered.'));
+       queries_redirect(ME . "table=" . urlencode($TABLE), lang('Indexes have been altered.'), alter_indexes($TABLE, $alter));
 }
 
 page_header(lang('Indexes'), $error, array("table" => $TABLE), $TABLE);
@@ -77,7 +81,7 @@ foreach ($row["indexes"] as $index) {
        ksort($index["columns"]);
        foreach ($index["columns"] as $i => $column) {
                echo "<span>" . html_select("indexes[$j][columns][$i]", array(-1 => "") + $fields, $column, ($i == count($index["columns"]) ? "indexesAddColumn(this);" : 1));
-               echo "<input name='indexes[$j][lengths][$i]' size='2' value='" . h($index["lengths"][$i]) . "'> </span>\n";
+               echo "<input name='indexes[$j][lengths][$i]' size='2' value='" . h($index["lengths"][$i]) . "'> </span>\n"; //! hide for non-MySQL drivers, add ASC|DESC
        }
        echo "\n";
        $j++;
index 8ca5dfe68b8a59930d7440c36a48fadbf11c6adb..0d5ad61e74e6eee00ad309e51ca30c19724d2949 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Nahrávání souborů není povoleno.',
        'Routine has been called, %d row(s) affected.' => array('Procedura byla zavolána, byl změněn %d záznam.', 'Procedura byla zavolána, byly změněny %d záznamy.', 'Procedura byla zavolána, bylo změněno %d záznamů.'),
        'Call' => 'Zavolat',
-       'No MySQL extension' => 'Žádná MySQL extenze',
+       'No extension' => 'Žádná extenze',
        'None of the supported PHP extensions (%s) are available.' => 'Není dostupná žádná z podporovaných PHP extenzí (%s).',
        'Session support must be enabled.' => 'Session proměnné musí být povolené.',
        'Session expired, please login again.' => 'Session vypršela, přihlašte se prosím znovu.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Vytvořit trigger',
        'Time' => 'Čas',
        'Event' => 'Událost',
-       'MySQL version: %s through PHP extension %s' => 'Verze MySQL: %s přes PHP extenzi %s',
+       '%s version: %s through PHP extension %s' => 'Verze %s: %s přes PHP extenzi %s',
        '%d row(s)' => array('%d řádek', '%d řádky', '%d řádků'),
-       '~ %s' => '~ %s',
        'Remove' => 'Odebrat',
        'Are you sure?' => 'Opravdu?',
        'Privileges' => 'Oprávnění',
@@ -203,7 +202,7 @@ $translations = array(
        'Select data' => 'Vypsat data',
        'Stop on error' => 'Zastavit při chybě',
        'Maximum number of allowed fields exceeded. Please increase %s and %s.' => 'Byl překročen maximální povolený počet polí. Zvyšte prosím %s a %s.',
-       '(anywhere)' => '(kdekoliv)',
+       'anywhere' => 'kdekoliv',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$6.$4.$1',
        '[yyyy]-mm-dd' => 'd.m.[rrrr]',
@@ -229,4 +228,9 @@ $translations = array(
        'File does not exist.' => 'Soubor neexistuje.',
        'Permanent login' => 'Trvalé přihlášení',
        '%d in total' => '%d celkem',
+       'Attachments' => 'Přílohy',
+       'System' => 'Systém',
+       'last' => 'poslední',
+       'Network' => 'Síť',
+       'Geometry' => 'Geometrie',
 );
index cca37cb78de2ba04add4b023debdb8362b0d8d46..2b2a695ad126b6ac86dd7cf2148a1f735b81d613 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Importieren von Dateien abgeschaltet.',
        'Routine has been called, %d row(s) affected.' => array('Kommando SQL ausgeführt, %d Datensatz betroffen.', 'Kommando SQL ausgeführt, %d Datensätze betroffen.'),
        'Call' => 'Aufrufen',
-       'No MySQL extension' => 'Keine MySQL-Erweiterungen installiert',
+       'No extension' => 'Keine Erweiterungen installiert',
        'None of the supported PHP extensions (%s) are available.' => 'Keine der unterstützten PHP-Erweiterungen (%s) ist vorhanden.',
        'Session support must be enabled.' => 'Sitzungen müssen aktiviert sein.',
        'Session expired, please login again.' => 'Sitzungsdauer abgelaufen, bitte erneut anmelden.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Trigger hinzufügen',
        'Time' => 'Zeitpunkt',
        'Event' => 'Ereignis',
-       'MySQL version: %s through PHP extension %s' => 'Version MySQL: %s, mit PHP-Erweiterung %s',
+       '%s version: %s through PHP extension %s' => 'Version %s: %s, mit PHP-Erweiterung %s',
        '%d row(s)' => array('%d Datensatz', '%d Datensätze'),
-       '~ %s' => '~ %s',
        'Remove' => 'Entfernen',
        'Are you sure?' => 'Sind Sie sicher ?',
        'Privileges' => 'Rechte',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Name der Partition',
        'Values' => 'Werte',
        '%d row(s) have been imported.' => array('%d Datensatz importiert.', '%d Datensätze wurden importiert.'),
-       'Show structure' => 'Tabellenstruktur',
-       '(anywhere)' => '(beliebig)',
+       'anywhere' => 'beliebig',
        'CSV Import' => 'Importiere CSV',
        'Import' => 'Importieren',
        'Stop on error' => 'Bei Fehler anhaltan',
-       'Select data' => 'Tabelle auswählen',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$6.$4.$1',
        '[yyyy]-mm-dd' => 't.m.[jjjj]',
index c539c432ffe67d975eee57d8c519974dc0fc4302..e394529420e48f35e86b882971e85a50fd8e7541 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Importación de archivos deshablilitado.',
        'Routine has been called, %d row(s) affected.' => array('Consulta ejecutada, %d registro afectado.', 'Consulta ejecutada, %d registros afectados.'),
        'Call' => 'Llamar',
-       'No MySQL extension' => 'No hay extension MySQL',
+       'No extension' => 'No hay extension',
        'None of the supported PHP extensions (%s) are available.' => 'Ninguna de las extensiones PHP soportadas (%s) está disponible.',
        'Session support must be enabled.' => 'Deben estar habilitadas las sesiones.',
        'Session expired, please login again.' => 'Sesión expirada, favor loguéese de nuevo.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Agregar Trigger',
        'Time' => 'Tiempo',
        'Event' => 'Evento',
-       'MySQL version: %s through PHP extension %s' => 'Versión MySQL: %s a través de extensión PHP %s',
+       '%s version: %s through PHP extension %s' => 'Versión %s: %s a través de extensión PHP %s',
        '%d row(s)' => array('%d registro', '%d registros'),
-       '~ %s' => '~ %s',
        'Remove' => 'Eliminar',
        'Are you sure?' => 'Está seguro?',
        'Privileges' => 'Privilegios',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Nombre de Partición',
        'Values' => 'Valores',
        '%d row(s) have been imported.' => array('%d registro importado.', '%d registros importados.'),
-       'Show structure' => 'Información de la Tabla',
-       '(anywhere)' => '(donde sea)',
+       'anywhere' => 'donde sea',
        'CSV Import' => 'Importar CSV',
        'Import' => 'Importar',
        'Stop on error' => 'Parar en caso de error',
-       'Select data' => 'Mostrar datos',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$5/$3/$1',
        '[yyyy]-mm-dd' => 'dd/mm/[aaaa]',
index 6f582c8f58f26f315f9146f6fb23825acf4622b0..5790f9a96798974b1acfb5bf85acfe27e1ea82a7 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Failide üleslaadimine on keelatud.',
        'Routine has been called, %d row(s) affected.' => array('Protseduur täideti edukalt, mõjutatud ridu: %d.', 'Protseduur täideti edukalt, mõjutatud ridu: %d.'),
        'Call' => 'Käivita',
-       'No MySQL extension' => 'Ei leitud MySQL laiendust',
+       'No extension' => 'Ei leitud laiendust',
        'None of the supported PHP extensions (%s) are available.' => 'Serveris pole ühtegi toetatud PHP laiendustest (%s).',
        'Session support must be enabled.' => 'Sessioonid peavad olema lubatud.',
        'Session expired, please login again.' => 'Sessioon on aegunud, palun logige uuesti sisse.',
@@ -129,7 +129,6 @@ $translations = array(
        'Time' => 'Aeg',
        'Event' => 'Sündmus',
        '%d row(s)' => array('%d rida', '%d rida'),
-       '~ %s' => '~ %s',
        'Remove' => 'Eemalda',
        'Are you sure?' => 'Kas oled kindel?',
        'Privileges' => 'Õigused',
@@ -142,7 +141,7 @@ $translations = array(
        'Routine' => 'Protseduur',
        'Grant' => 'Anna',
        'Revoke' => 'Eemalda',
-       'MySQL version: %s through PHP extension %s' => 'MySQL versioon: %s, kasutatud PHP moodul: %s',
+       '%s version: %s through PHP extension %s' => '%s versioon: %s, kasutatud PHP moodul: %s',
        'Logged as: %s' => 'Sisse logitud: %s',
        'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'POST-andmete maht on liialt suur. Palun vähendage andmeid või suurendage %s php-seadet.',
        'Move up' => 'Liiguta ülespoole',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Partitsiooni nimi',
        'Values' => 'Väärtused',
        '%d row(s) have been imported.' => array('Imporditi %d rida.', 'Imporditi %d rida.'),
-       'Show structure' => 'Tabeli struktuur',
-       '(anywhere)' => '(vahet pole)',
+       'anywhere' => 'vahet pole',
        'CSV Import' => 'Impordi CSV',
        'Import' => 'Impordi',
        'Stop on error' => 'Peatuda vea esinemisel',
-       'Select data' => 'Vali tabel',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$6.$4.$1',
        '[yyyy]-mm-dd' => 'd.m.[yyyy]',
index e6dd1030df6c6dbac013984dbd30d3ecde9ec183..84a89c51ce9a273b7e61a542988da813d216c274 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Importation de fichier désactivé.',
        'Routine has been called, %d row(s) affected.' => array('Routine exécutée, %d ligne modifiée.', 'Routine exécutée, %d lignes modifiées.'),
        'Call' => 'Appeler',
-       'No MySQL extension' => 'Extension MySQL introuvable',
+       'No extension' => 'Extension introuvable',
        'None of the supported PHP extensions (%s) are available.' => 'Aucune des extensions PHP supportées (%s) n\'est disponible.',
        'Session support must be enabled.' => 'Veuillez activer les sessions.',
        'Session expired, please login again.' => 'Session expirée, veuillez vous authentifier à nouveau.',
@@ -129,7 +129,6 @@ $translations = array(
        'Time' => 'Temps',
        'Event' => 'Évènement',
        '%d row(s)' => array('%d ligne', '%d lignes'),
-       '~ %s' => '~ %s',
        'Remove' => 'Effacer',
        'Are you sure?' => 'Êtes-vous certain?',
        'Privileges' => 'Privilège',
@@ -142,7 +141,7 @@ $translations = array(
        'Routine' => 'Routine',
        'Grant' => 'Grant',
        'Revoke' => 'Revoke',
-       'MySQL version: %s through PHP extension %s' => 'Version de MySQL: %s utilisant l\'extension %s',
+       '%s version: %s through PHP extension %s' => 'Version de %s: %s utilisant l\'extension %s',
        'Logged as: %s' => 'Authentifié en tant que %s',
        'Too big POST data. Reduce the data or increase the %s configuration directive.' => 'Donnée POST trop grande . Réduire la taille des données ou modifier le %s dans la configuration de PHP.',
        'Move up' => 'Déplacer vers le haut',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Nom de la partition',
        'Values' => 'Valeurs',
        '%d row(s) have been imported.' => array('%d ligne a été importé.','%d lignes ont été importé.'),
-       'Show structure' => 'Structure de la table',
-       '(anywhere)' => '(n\'importe où)',
+       'anywhere' => 'n\'importe où',
        'CSV Import' => 'Importation CVS',
        'Import' => 'Importer',
        'Stop on error' => 'Arrêt sur erreur',
-       'Select data' => 'Selectionner la table',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$5/$3/$1',
        '[yyyy]-mm-dd' => 'jj/mm/[aaaa]',
index 7772750405723800c0925ce56954b26850385191..7eb551b0e09a5a52909ca7ec833ff1a2e9994588 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Caricamento file disabilitato.',
        'Routine has been called, %d row(s) affected.' => array('Routine chiamata, %d riga interessata.', 'Routine chiamata, %d righe interessate.'),
        'Call' => 'Chiama',
-       'No MySQL extension' => 'Estensioni MySQL non presenti',
+       'No extension' => 'Estensioni non presenti',
        'None of the supported PHP extensions (%s) are available.' => 'Nessuna delle estensioni PHP supportate (%s) disponibile.',
        'Session support must be enabled.' => 'Le sessioni devono essere abilitate.',
        'Session expired, please login again.' => 'Sessione scaduta, autenticarsi di nuovo.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Crea trigger',
        'Time' => 'Orario',
        'Event' => 'Evento',
-       'MySQL version: %s through PHP extension %s' => 'Versione MySQL: %s via estensione PHP %s',
+       '%s version: %s through PHP extension %s' => 'Versione %s: %s via estensione PHP %s',
        '%d row(s)' => array('%d riga', '%d righe'),
-       '~ %s' => '~ %s',
        'Remove' => 'Rimuovi',
        'Are you sure?' => 'Sicuro?',
        'Privileges' => 'Privilegi',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Nome partizione',
        'Values' => 'Valori',
        '%d row(s) have been imported.' => array('%d riga importata.','%d righe importate.'),
-       'Show structure' => 'Struttura tabella',
-       '(anywhere)' => '(ovunque)',
+       'anywhere' => 'ovunque',
        'CSV Import' => 'Importa da CSV',
        'Import' => 'Importa',
        'Stop on error' => 'Stop su errore',
-       'Select data' => 'Scegli tabella',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$5/$3/$1',
        '[yyyy]-mm-dd' => 'dd/mm/[yyyy]',
index 6f3b6a8131a5f194e25595dbec5d5856641b1e86..d319fbb0846b593776a7cbbf89e18aabd247a3e5 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Bestanden uploaden is uitgeschakeld.',
        'Routine has been called, %d row(s) affected.' => array('Procedure uitgevoerd, %d rij geraakt.', 'Procedure uitgevoerd, %d rijen geraakt.'),
        'Call' => 'Uitvoeren',
-       'No MySQL extension' => 'Geen MySQL extensie',
+       'No extension' => 'Geen extensie',
        'None of the supported PHP extensions (%s) are available.' => 'Geen geldige PHP extensies beschikbaar (%s).',
        'Session support must be enabled.' => 'Sessies moeten geactiveerd zijn.',
        'Session expired, please login again.' => 'Uw sessie is verlopen. Gelieve opnieuw in te loggen.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Trigger aanmaken',
        'Time' => 'Time',
        'Event' => 'Event',
-       'MySQL version: %s through PHP extension %s' => 'MySQL versie: %s met PHP extensie %s',
+       '%s version: %s through PHP extension %s' => '%s versie: %s met PHP extensie %s',
        '%d row(s)' => array('%d rij', '%d rijen'),
-       '~ %s' => '~ %s',
        'Remove' => 'Verwijderen',
        'Are you sure?' => 'Weet u het zeker?',
        'Privileges' => 'Rechten',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => 'Partitie naam',
        'Values' => 'Waarden',
        '%d row(s) have been imported.' => array('%d rij werd geïmporteerd.', '%d rijen werden geïmporteerd.'),
-       'Show structure' => 'Tabelstructuur',
-       '(anywhere)' => '(overal)',
+       'anywhere' => 'overal',
        'CSV Import' => 'CSV Import',
        'Import' => 'Importeren',
        'Stop on error' => 'Stoppen bij fout',
-       'Select data' => 'Selecteer tabel',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$5-$3-$1',
        '[yyyy]-mm-dd' => 'dd-mm-[jjjj]',
index 52c40404738d3c9eff7dbc88434c0c890be7d762..01877c61fb3a56f9e1ffc8358e193dbe89b6a3a2 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Загрузка файлов на сервер запрещена.',
        'Routine has been called, %d row(s) affected.' => array('Была вызвана процедура, %d запись была изменена.', 'Была вызвана процедура, %d записи было изменено.', 'Была вызвана процедура, %d записей было изменено.'),
        'Call' => 'Вызвать',
-       'No MySQL extension' => 'Нет MySQL расширений',
+       'No extension' => 'Нет расширений',
        'None of the supported PHP extensions (%s) are available.' => 'Не доступно ни одного расширения из поддерживаемых (%s).',
        'Session support must be enabled.' => 'Сессии должны быть включены.',
        'Session expired, please login again.' => 'Срок действия сесси истек, нужно снова войти в систему.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Создать триггер',
        'Time' => 'Время',
        'Event' => 'Событие',
-       'MySQL version: %s through PHP extension %s' => 'Версия MySQL: %s с PHP-расширением %s',
+       '%s version: %s through PHP extension %s' => 'Версия %s: %s с PHP-расширением %s',
        '%d row(s)' => array('%d строка', '%d строки', '%d строк'),
-       '~ %s' => '~ %s',
        'Remove' => 'Удалить',
        'Are you sure?' => 'Вы уверены?',
        'Privileges' => 'Полномочия',
@@ -199,11 +198,9 @@ $translations = array(
        '%d row(s) have been imported.' => array('Импортирована %d строка.', 'Импортировано %d строки.', 'Импортировано %d строк.'),
        'CSV Import' => 'Импорт CSV',
        'Import' => 'Импорт',
-       'Show structure' => 'Структура таблицы',
-       'Select data' => 'Выбрать данные из таблицы',
        'Stop on error' => 'Остановить при ошибке',
        'Maximum number of allowed fields exceeded. Please increase %s and %s.' => 'Достигнуто максимальное значение количества доступных полей. Увеличьте %s и %s.',
-       '(anywhere)' => '(в любом месте)',
+       'anywhere' => 'в любом месте',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$5.$3.$1',
        '[yyyy]-mm-dd' => 'дд.мм.[гггг]',
index cd492153d7cbe03ca296d77e754f4d3199387257..5a6acd3d58a0fb818d1fb954b5fe3b4cd02e3716 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => 'Nahrávánie súborov nie je povolené.',
        'Routine has been called, %d row(s) affected.' => array('Procedúra bola zavolaná, bol zmenený %d záznam.', 'Procedúra bola zavolaná, boli zmenené %d záznamy.', 'Procedúra bola zavolaná, bolo zmenených %d záznamov.'),
        'Call' => 'Zavolať',
-       'No MySQL extension' => 'Žiadne MySQL rozšírenie',
+       'No extension' => 'Žiadne rozšírenie',
        'None of the supported PHP extensions (%s) are available.' => 'Nie je dostupné žiadne z podporovaných rozšírení (%s).',
        'Session support must be enabled.' => 'Session premenné musia byť povolené.',
        'Session expired, please login again.' => 'Session vypršala, prihláste sa prosím znova.',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => 'Vytvoriť trigger',
        'Time' => 'Čas',
        'Event' => 'Udalosť',
-       'MySQL version: %s through PHP extension %s' => 'Verzia MySQL: %s cez PHP rozšírenie %s',
+       '%s version: %s through PHP extension %s' => 'Verzia %s: %s cez PHP rozšírenie %s',
        '%d row(s)' => array('%d riadok', '%d riadky', '%d riadkov'),
-       '~ %s' => '~ %s',
        'Remove' => 'Odobrať',
        'Are you sure?' => 'Naozaj?',
        'Privileges' => 'Oprávnenia',
@@ -199,11 +198,9 @@ $translations = array(
        '%d row(s) have been imported.' => array('Bol importovaný %d záznam.', 'Boli importované %d záznamy.', 'Bolo importovaných %d záznamov.'),
        'CSV Import' => 'Import CSV',
        'Import' => 'Import',
-       'Show structure' => 'Štruktúra tabuľky',
-       'Select data' => 'Vypísať tabuľku',
        'Stop on error' => 'Zastaviť pri chybe',
        'Maximum number of allowed fields exceeded. Please increase %s and %s.' => 'Bol prekročený maximálny počet povolených polí. Zvýšte prosím %s a %s.',
-       '(anywhere)' => '(kdekoľvek)',
+       'anywhere' => 'kdekoľvek',
        '%.3f s' => '%.3f s',
        '$1-$3-$5' => '$6.$4.$1',
        '[yyyy]-mm-dd' => 'd.m.[rrrr]',
index fa4f6320688d250fc4f9bdacd5db518ce636e282..50a8a83143527839c8501ecfff897c0ae8e96699 100644 (file)
@@ -79,7 +79,7 @@ $translations = array(
        'File uploads are disabled.' => '檔案上傳被禁用。',
        'Routine has been called, %d row(s) affected.' => '程序已被執行,%d行被影響',
        'Call' => '呼叫',
-       'No MySQL extension' => '沒有 MySQL 擴充模組',
+       'No extension' => '沒有 擴充模組',
        'None of the supported PHP extensions (%s) are available.' => '沒有任何支援的PHP擴充模組(%s)。',
        'Session support must be enabled.' => 'Session 必須被啟用。',
        'Session expired, please login again.' => 'Session 已過期,請重新登入。',
@@ -126,9 +126,8 @@ $translations = array(
        'Create trigger' => '建立觸發器',
        'Time' => '時間',
        'Event' => '事件',
-       'MySQL version: %s through PHP extension %s' => 'MySQL版本:%s 透過PHP擴充模組 %s',
+       '%s version: %s through PHP extension %s' => '%s版本:%s 透過PHP擴充模組 %s',
        '%d row(s)' => '%d行',
-       '~ %s' => '~ %s',
        'Remove' => '移除',
        'Are you sure?' => '你確定嗎?',
        'Privileges' => '權限',
@@ -196,12 +195,10 @@ $translations = array(
        'Partition name' => '分區名',
        'Values' => '值',
        '%d row(s) have been imported.' => '%d行已導入。',
-       'Show structure' => '資料表結構',
-       '(anywhere)' => '(任意位置)',
+       'anywhere' => '任意位置',
        'CSV Import' => '匯入 CSV',
        'Import' => '匯入',
        'Stop on error' => '出錯時停止',
-       'Select data' => '選擇資料表',
        '%.3f s' => '%.3f秒',
        '$1-$3-$5' => '$1.$3.$5',
        '[yyyy]-mm-dd' => '[yyyy].mm.dd',
index e86396bc88bd0ecd1adacf5a2254d62bfc83a772..7472eb7207d35b8ae1f648e21d9339cf9f36dc9b 100644 (file)
@@ -81,7 +81,7 @@ $translations = array(
        'File uploads are disabled.' => '文件上传被禁用。',
        'Routine has been called, %d row(s) affected.' => '子程序被调用,%d 行被影响',
        'Call' => '调用',
-       'No MySQL extension' => '没有MySQL扩展',
+       'No extension' => '没有扩展',
        'None of the supported PHP extensions (%s) are available.' => '没有支持的 PHP 扩展可用(%s)。',
        'Session support must be enabled.' => '会话必须被启用。',
        'Session expired, please login again.' => '会话已过期,请重新登录。',
@@ -128,9 +128,8 @@ $translations = array(
        'Create trigger' => '创建触发器',
        'Time' => '时间',
        'Event' => '事件',
-       'MySQL version: %s through PHP extension %s' => 'MySQL 版本:%s 通过 PHP 扩展 %s',
+       '%s version: %s through PHP extension %s' => '%s 版本:%s 通过 PHP 扩展 %s',
        '%d row(s)' => '%d 行',
-       '~ %s' => '~ %s',
        'Remove' => '移除',
        'Are you sure?' => '你确定吗?',
        'Privileges' => '权限',
@@ -198,12 +197,10 @@ $translations = array(
        'Partition name' => '分区名',
        'Values' => '值',
        '%d row(s) have been imported.' => '%d 行已导入。',
-       'Show structure' => '表结构',
-       '(anywhere)' => '(任意位置)',
+       'anywhere' => '任意位置',
        'CSV Import' => 'CSV 导入',
        'Import' => '导入',
        'Stop on error' => '出错时停止',
-       'Select data' => '选择表',
        '%.3f s' => '%.3f 秒',
        '$1-$3-$5' => '$1.$3.$5',
        '[yyyy]-mm-dd' => '[yyyy].mm.dd',
index 77155a379185fd4aa2525884b85803002ca70a73..7bbf361657a2653835d9eee66d391b70c9e7bcbe 100644 (file)
@@ -5,8 +5,7 @@ $result = $connection->query("SELECT User, Host FROM mysql.user ORDER BY Host, U
 if (!$result) {
        ?>
 <form action=""><p>
-<?php echo SID_FORM; ?>
-<?php if ($_GET["server"] != "") { ?><input type="hidden" name="server" value="<?php echo h($_GET["server"]); ?>"><?php } ?>
+<?php hidden_fields_get(); ?>
 <?php echo lang('Username'); ?>: <input name="user">
 <?php echo lang('Server'); ?>: <input name="host" value="localhost">
 <input type="hidden" name="grant" value="">
index 00252bfd2811772792fd35673476774c71bc4784..316abdf08b11bf49cc6f439162ee5a85b7e8720d 100644 (file)
@@ -7,7 +7,7 @@ $table_pos_js = array();
 preg_match_all('~([^:]+):([-0-9.]+)x([-0-9.]+)(_|$)~', $_COOKIE["adminer_schema"], $matches, PREG_SET_ORDER); //! ':' in table name
 foreach ($matches as $i => $match) {
        $table_pos[$match[1]] = array($match[2], $match[3]);
-       $table_pos_js[] = "\n\t'" . addcslashes($match[1], "\r\n'\\") . "': [ $match[2], $match[3] ]";
+       $table_pos_js[] = "\n\t'" . addcslashes($match[1], "\r\n'\\/") . "': [ $match[2], $match[3] ]";
 }
 
 $top = 0;
@@ -27,7 +27,7 @@ foreach (table_status() as $row) {
                $schema[$row["Name"]]["fields"][$name] = $field;
        }
        $schema[$row["Name"]]["pos"] = ($table_pos[$row["Name"]] ? $table_pos[$row["Name"]] : array($top, 0));
-       if ($row["Engine"] == "InnoDB") {
+       if (fk_support($row)) {
                foreach (foreign_keys($row["Name"]) as $val) {
                        if (!$val["db"]) {
                                $left = $base_left;
index 76604d3fdc437509dafb9b75d0a6d42e365822b5..eaab9b97050bdd06c242a2d2d02411f55dcf963d 100644 (file)
@@ -26,6 +26,12 @@ $limit = $adminer->selectLimitProcess();
 $from = ($select ? implode(", ", $select) : "*") . "\nFROM " . idf_escape($TABLE) . ($where ? "\nWHERE " . implode(" AND ", $where) : "");
 $group_by = ($group && count($group) < count($select) ? "\nGROUP BY " . implode(", ", $group) : "") . ($order ? "\nORDER BY " . implode(", ", $order) : "");
 
+if ($_GET["page"] == "last") {
+       session_write_close();
+       $found_rows = $connection->result("SELECT COUNT(*) FROM " . idf_escape($TABLE) . ($where ? " WHERE " . implode(" AND ", $where) : ""));
+       redirect(remove_from_uri("page") . ($found_rows > $limit ? "&page=" . floor(($found_rows - 1) / $limit) : ""));
+}
+
 if ($_POST && !$error) {
        $where_check = "(" . implode(") OR (", array_map('where_check', (array) $_POST["check"])) . ")";
        $primary = ($indexes["PRIMARY"] ? ($select ? array_flip($indexes["PRIMARY"]["columns"]) : array()) : null); // empty array means that all primary fields are selected
@@ -43,7 +49,7 @@ if ($_POST && !$error) {
                        if ($select) {
                                $row = array();
                                foreach ($select as $val) {
-                                       $row[] = (ereg('^`(.*)`$', $val, $match) ? idf_unescape($match[1]) : $val); //! columns looking like functions
+                                       $row[] = (ereg('^`.*`$', $val) ? idf_unescape($val) : $val); //! columns looking like functions
                                }
                        }
                        dump_csv($row);
@@ -54,7 +60,7 @@ if ($_POST && !$error) {
                        $union = array();
                        foreach ($_POST["check"] as $val) {
                                // where is not unique so OR can't be used
-                               $union[] = "(SELECT $from " . ($where ? "AND " : "WHERE ") . where_check($val) . $group_by . " LIMIT 1)";
+                               $union[] = "(SELECT" . limit("$from " . ($where ? "AND " : "WHERE ") . where_check($val) . $group_by, 1) . ")";
                        }
                        dump_data($TABLE, "INSERT", implode(" UNION ALL ", $union));
                }
@@ -64,27 +70,38 @@ if ($_POST && !$error) {
                if (!$_POST["import"]) { // edit
                        $result = true;
                        $affected = 0;
-                       $command = ($_POST["delete"] ? "DELETE FROM " : ($_POST["clone"] ? "INSERT INTO " : "UPDATE ")) . idf_escape($TABLE);
+                       $query = idf_escape($TABLE);
                        $set = array();
                        if (!$_POST["delete"]) {
                                foreach ($columns as $name => $val) { //! should check also for edit or insert privileges
                                        $val = process_input($fields[$name]);
-                                       if ($_POST["clone"]) {
-                                               $set[idf_escape($name)] = ($val !== false ? $val : idf_escape($name));
-                                       } elseif ($val !== false) {
-                                               $set[] = idf_escape($name) . " = $val";
+                                       if ($val !== null) {
+                                               if ($_POST["clone"]) {
+                                                       $set[idf_escape($name)] = ($val !== false ? $val : idf_escape($name));
+                                               } elseif ($val !== false) {
+                                                       $set[] = idf_escape($name) . " = $val";
+                                               }
                                        }
                                }
-                               $command .= ($_POST["clone"] ? " (" . implode(", ", array_keys($set)) . ")\nSELECT " . implode(", ", $set) . "\nFROM " . idf_escape($TABLE) : " SET\n" . implode(",\n", $set));
+                               $query .= ($_POST["clone"] ? " (" . implode(", ", array_keys($set)) . ")\nSELECT " . implode(", ", $set) . "\nFROM " . idf_escape($TABLE) : " SET\n" . implode(",\n", $set));
                        }
                        if ($_POST["delete"] || $set) {
-                               if ($_POST["all"] || ($primary === array() && $_POST["check"])) {
-                                       $result = queries($command . ($_POST["all"] ? ($where ? "\nWHERE " . implode(" AND ", $where) : "") : "\nWHERE $where_check"));
+                               $command = "UPDATE";
+                               if ($_POST["delete"]) {
+                                       $command = "DELETE";
+                                       $query = "FROM $query";
+                               }
+                               if ($_POST["clone"]) {
+                                       $command = "INSERT";
+                                       $query = "INTO $query";
+                               }
+                               if ($_POST["all"] || ($primary === array() && $_POST["check"]) || count($group) < count($select)) {
+                                       $result = queries($command . " $query" . ($_POST["all"] ? ($where ? "\nWHERE " . implode(" AND ", $where) : "") : "\nWHERE $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 . "\nWHERE " . where_check($val) . (count($group) < count($select) ? "" : "\nLIMIT 1"));
+                                               $result = queries($command . limit1($query . "\nWHERE " . where_check($val)));
                                                if (!$result) {
                                                        break;
                                                }
@@ -101,8 +118,9 @@ if ($_POST && !$error) {
                        preg_match_all('~(?>"[^"]*"|[^"\\r\\n]+)+~', $file, $matches);
                        $affected = count($matches[0]);
                        queries("START TRANSACTION");
+                       $separator = ($_POST["separator"] == ";" ? ";" : ",");
                        foreach ($matches[0] as $key => $val) {
-                               preg_match_all('~(("[^"]*")+|[^,]*),~', "$val,", $matches2);
+                               preg_match_all("~((\"[^\"]*\")+|[^$separator]*)$separator~", $val . $separator, $matches2);
                                if (!$key && !array_diff($matches2[1], $cols)) { //! doesn't work with column names containing ",\n
                                        // first row corresponds to column names - use it for table structure
                                        $cols = $matches2[1];
@@ -120,7 +138,7 @@ if ($_POST && !$error) {
                                }
                        }
                        if ($result) {
-                               queries("COMMIT");
+                       queries("COMMIT");
                        }
                        queries_redirect(remove_from_uri("page"), lang('%d row(s) have been imported.', $affected), $result);
                        queries("ROLLBACK");
@@ -147,11 +165,11 @@ if (isset($rights["insert"])) {
 $adminer->selectLinks($table_status, $set);
 
 if (!$columns) {
-       echo "<p class='error'>" . lang('Unable to select the table') . ($fields ? "" : ": " . error()) . ".\n";
+       echo "<p class='error'>" . lang('Unable to select the table') . ($fields ? "." : ": " . error()) . "\n";
 } else {
        echo "<form action='' id='form'>\n";
        echo "<div style='display: none;'>";
-       echo ($_GET["server"] != "" ? '<input type="hidden" name="server" value="' . h($_GET["server"]) . '">' : "");
+       hidden_fields_get();
        echo (DB != "" ? '<input type="hidden" name="db" value="' . h(DB) . '">' : ""); // not used in Editor
        echo '<input type="hidden" name="select" value="' . h($TABLE) . '">';
        echo "</div>\n";
@@ -163,7 +181,7 @@ if (!$columns) {
        $adminer->selectActionPrint($text_length);
        echo "</form>\n";
        
-       $query = "SELECT " . (intval($limit) && $group && count($group) < count($select) ? "SQL_CALC_FOUND_ROWS " : "") . $from . $group_by . ($limit != "" ? "\nLIMIT " . intval($limit) . ($_GET["page"] ? " OFFSET " . ($limit * $_GET["page"]) : "") : "");
+       $query = "SELECT" . limit((intval($limit) && $group && count($group) < count($select) && $driver == "sql" ? "SQL_CALC_FOUND_ROWS " : "") . $from . $group_by, ($limit != "" ? intval($limit) : null), ($_GET["page"] ? $limit * $_GET["page"] : 0));
        echo $adminer->selectQuery($query);
        
        $result = $connection->query($query);
@@ -172,19 +190,19 @@ if (!$columns) {
        } else {
                $email_fields = array();
                echo "<form action='' method='post' enctype='multipart/form-data'>\n";
-               if (!$result->num_rows) {
+               $rows = array();
+               while ($row = $result->fetch_assoc()) {
+                       $rows[] = $row;
+               }
+               // use count($rows) without LIMIT, COUNT(*) without grouping, FOUND_ROWS otherwise (slowest)
+               $found_rows = (intval($limit) && $group && count($group) < count($select)
+                       ? ($driver == "sql" ? $connection->result(" SELECT FOUND_ROWS()") : $connection->result("SELECT COUNT(*) FROM ($query) x")) // space to allow mysql.trace_mode
+                       : count($rows)
+               );
+               
+               if (!$rows) {
                        echo "<p class='message'>" . lang('No rows.') . "\n";
                } else {
-                       $rows = array();
-                       while ($row = $result->fetch_assoc()) {
-                               $rows[] = $row;
-                       }
-                       // use count($rows) without LIMIT, COUNT(*) without grouping, FOUND_ROWS otherwise (slowest)
-                       $found_rows = (intval($limit) && $group && count($group) < count($select)
-                               ? $connection->result($connection->query(" SELECT FOUND_ROWS()")) // space to allow mysql.trace_mode
-                               : count($rows)
-                       );
-                       
                        $backward_keys = $adminer->backwardKeys($TABLE, $table_name);
                        
                        echo "<table cellspacing='0' class='nowrap' onclick='tableClick(event);'>\n";
@@ -205,7 +223,7 @@ if (!$columns) {
                        }
                        echo ($backward_keys ? "<th>" . lang('Relations') : "") . "</thead>\n";
                        foreach ($adminer->rowDescriptions($rows, $foreign_keys) as $n => $row) {
-                               $unique_array = unique_array($row, $indexes);
+                               $unique_array = unique_array($rows[$n], $indexes);
                                $unique_idf = "";
                                foreach ($unique_array as $key => $val) {
                                        $unique_idf .= "&" . (isset($val) ? urlencode("where[" . bracket_escape($key) . "]") . "=" . urlencode($val) : "null%5B%5D=" . urlencode($key));
@@ -222,7 +240,7 @@ if (!$columns) {
                                                if (!isset($val)) {
                                                        $val = "<i>NULL</i>";
                                                } else {
-                                                       if (ereg('blob|binary', $field["type"]) && $val != "") {
+                                                       if (ereg('binary|blob|bytea', $field["type"]) && $val != "") {
                                                                $link = h(ME . 'download=' . urlencode($TABLE) . '&field=' . urlencode($key) . $unique_idf);
                                                        }
                                                        if ($val == "") {
@@ -271,33 +289,45 @@ if (!$columns) {
                                echo "</tr>\n"; // close to allow white-space: pre
                        }
                        echo "</table>\n";
-                       
-                       if (intval($limit) && count($group) >= count($select)) {
-                               // slow with big tables
-                               ob_flush();
-                               flush();
-                               $found_rows = $connection->result($connection->query("SELECT COUNT(*) FROM " . idf_escape($TABLE) . ($where ? " WHERE " . implode(" AND ", $where) : "")));
+               }
+               
+               parse_str($_COOKIE["adminer_export"], $adminer_export);
+               
+               if ($rows || $_GET["page"]) {
+                       $exact_count = true;
+                       if (intval($limit) && count($group) >= count($select) && ($found_rows >= $limit || $_GET["page"])) {
+                               $found_rows = $table_status["Rows"];
+                               if (!isset($found_rows) || $where || $_GET["page"] * $limit * 2 > $found_rows || ($table_status["Engine"] == "InnoDB" && $found_rows < 1e5)) {
+                                       // slow with big tables
+                                       ob_flush();
+                                       flush();
+                                       $found_rows = $connection->result("SELECT COUNT(*) FROM " . idf_escape($TABLE) . ($where ? " WHERE " . implode(" AND ", $where) : ""));
+                               } else {
+                                       $exact_count = false;
+                               }
                        }
                        echo "<p class='pages'>";
                        if (intval($limit) && $found_rows > $limit) {
-                               // display first, previous 3, next 3 and last page
+                               // display first, previous 5, next 5 and last page
                                $max_page = floor(($found_rows - 1) / $limit);
-                               echo lang('Page') . ":" . pagination(0) . ($_GET["page"] > 3 ? " ..." : "");
-                               for ($i = max(1, $_GET["page"] - 2); $i < min($max_page, $_GET["page"] + 3); $i++) {
+                               echo lang('Page') . ":" . pagination(0) . ($_GET["page"] > 5 ? " ..." : "");
+                               for ($i = max(1, $_GET["page"] - 2); $i < min($max_page, $_GET["page"] + 5); $i++) {
                                        echo pagination($i);
                                }
-                               echo ($_GET["page"] + 3 < $max_page ? " ..." : "") . pagination($max_page);
+                               echo ($_GET["page"] + 5 < $max_page ? " ..." : "") . ($exact_count ? pagination($max_page) : ' <a href="' . h(remove_from_uri() . "&page=last") . '">' . lang('last') . "</a>");
                        }
-                       echo " (" . lang('%d row(s)', $found_rows) . ") " . checkbox("all", 1, 0, lang('whole result')) . "\n";
+                       echo " (" . ($exact_count ? "" : "~ ") . lang('%d row(s)', $found_rows) . ") " . checkbox("all", 1, 0, lang('whole result')) . "\n";
                        
                        echo (information_schema(DB) ? "" : "<fieldset><legend>" . lang('Edit') . "</legend><div><input type='submit' name='edit' value='" . lang('Edit') . "'> <input type='submit' name='clone' value='" . lang('Clone') . "'> <input type='submit' name='delete' value='" . lang('Delete') . "' onclick=\"return confirm('" . lang('Are you sure?') . " (' + (this.form['all'].checked ? $found_rows : formChecked(this, /check/)) + ')');\"></div></fieldset>\n");
                        print_fieldset("export", lang('Export'));
-                       echo $adminer->dumpOutput(1) . " " . $adminer->dumpFormat(1); // 1 - select
+                       echo $adminer->dumpOutput(1, $adminer_export["output"]) . " " . $adminer->dumpFormat(1, $adminer_export["format"]); // 1 - select
                        echo " <input type='submit' name='export' value='" . lang('Export') . "'>\n";
                        echo "</div></fieldset>\n";
                }
                print_fieldset("import", lang('CSV Import'), !$result->num_rows);
-               echo "<input type='hidden' name='token' value='$token'><input type='file' name='csv_file'> <input type='submit' name='import' value='" . lang('Import') . "'>\n";
+               echo "<input type='hidden' name='token' value='$token'><input type='file' name='csv_file'> ";
+               echo html_select("separator", array(",", ";"), ($adminer_export["format"] == "csv;" ? ";" : ","), 1); // 1 - select
+               echo " <input type='submit' name='import' value='" . lang('Import') . "'>\n";
                echo "</div></fieldset>\n";
                
                $adminer->selectEmailPrint(array_filter($email_fields, 'strlen'), $columns);
index 180b2721852e844f5395c6481f379168bd534851..d3abdeff3f7f78f7b9ee148dfbed30c4cf1d48ce 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 restart_session();
-$history = &$_SESSION["history"][$_GET["server"]][DB];
+$history_all = &get_session("history");
+$history = &$history_all[DB];
 if (!$error && $_POST["clear"]) {
        $history = array();
        redirect(remove_from_uri("history"));
@@ -28,15 +29,14 @@ if (!$error && $_POST) {
                        $history[] = $query;
                }
                $space = "(\\s|/\\*.*\\*/|(#|-- )[^\n]*\n|--\n)";
-               $alter_database = "(CREATE|DROP)$space+(DATABASE|SCHEMA)\\b~isU";
-               if (!ini_get("session.use_cookies")) {
+               if (!ini_bool("session.use_cookies")) {
                        session_write_close();
                }
                $delimiter = ";";
                $offset = 0;
                $empty = true;
-               $connection2 = (DB != "" ? connect() : null); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
-               if (is_object($connection2)) {
+               $connection2 = connect(); // connection for exploring indexes and EXPLAIN (to not replace FOUND_ROWS()) //! PDO - silent error
+               if (is_object($connection2) && DB != "") {
                        $connection2->select_db(DB);
                }
                $queries = 0;
@@ -59,7 +59,7 @@ if (!$error && $_POST) {
                                                $empty = false;
                                                $q = substr($query, 0, $match[0][1]);
                                                $queries++;
-                                               echo "<pre class='jush-sql' id='sql-$queries'>" . shorten_utf8(trim($q), 1000) . "</pre>\n";
+                                               echo "<pre class='jush-$driver' id='sql-$queries'>" . shorten_utf8(trim($q), 1000) . "</pre>\n";
                                                ob_flush();
                                                flush(); // can take a long time - show the running query
                                                $start = explode(" ", microtime()); // microtime(true) is available since PHP 5
@@ -71,6 +71,9 @@ if (!$error && $_POST) {
                                                                break;
                                                        }
                                                } else {
+                                                       if (is_object($connection2) && preg_match("~^$space*(USE)\\b~isU", $q)) {
+                                                               $connection2->query($q);
+                                                       }
                                                        do {
                                                                $result = $connection->store_result();
                                                                $end = explode(" ", microtime());
@@ -82,18 +85,17 @@ if (!$error && $_POST) {
                                                                                $id = "explain-$queries";
                                                                                echo ", <a href='#$id' onclick=\"return !toggle('$id');\">EXPLAIN</a>\n";
                                                                                echo "<div id='$id' class='hidden'>\n";
-                                                                               select($connection2->query("EXPLAIN $q"));
+                                                                               select(explain($connection2, $q));
                                                                                echo "</div>\n";
                                                                        }
                                                                } else {
-                                                                       if (preg_match("~^$space*$alter_database", $query)) {
+                                                                       if (preg_match("~^$space*(CREATE|DROP|ALTER)$space+(DATABASE|SCHEMA)\\b~isU", $q)) {
                                                                                restart_session();
-                                                                               $_SESSION["databases"][$_GET["server"]] = null; // clear cache
+                                                                               set_session("databases", null); // clear cache
                                                                                session_write_close();
                                                                        }
                                                                        echo "<p class='message' title='" . h($connection->info) . "'>" . lang('Query executed OK, %d row(s) affected.', $connection->affected_rows) . "$time\n";
                                                                }
-                                                               unset($result); // free resultset
                                                                $start = $end;
                                                        } while ($connection->next_result());
                                                }
@@ -119,6 +121,7 @@ if (!$error && $_POST) {
                if ($empty) {
                        echo "<p class='message'>" . lang('No commands to execute.') . "\n";
                }
+               //! MS SQL - SET SHOWPLAN_ALL OFF
        } else {
                echo "<p class='error'>" . upload_error($query) . "\n";
        }
@@ -142,7 +145,7 @@ echo h($q);
 
 <p>
 <?php
-if (!ini_get("file_uploads")) {
+if (!ini_bool("file_uploads")) {
        echo lang('File uploads are disabled.');
 } else { ?>
 <?php echo lang('File upload'); ?>: <input type="file" name="sql_file">
@@ -164,7 +167,7 @@ if ($history) {
        print_fieldset("history", lang('History'), $_GET["history"] != "");
        foreach ($history as $key => $val) {
                //! save and display timestamp
-               echo '<a href="' . h(ME . "sql=&history=$key") . '">' . lang('Edit') . '</a> <code class="jush-sql">' . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace('~^(#|-- ).*~m', '', $val)))), 80, "</code>") . "<br>\n";
+               echo '<a href="' . h(ME . "sql=&history=$key") . '">' . lang('Edit') . "</a> <code class='jush-$driver'>" . shorten_utf8(ltrim(str_replace("\n", " ", str_replace("\r", "", preg_replace('~^(#|-- ).*~m', '', $val)))), 80, "</code>") . "<br>\n";
        }
        echo "<input type='submit' name='clear' value='" . lang('Clear') . "'>\n";
        echo "</div></fieldset>\n";
index 567e4e6d44f8e984439a36942a65d5018af0c3c1..0ccbb81aa4182f00a7556ad5f66d35dbef292358 100644 (file)
@@ -4,26 +4,28 @@ a:visited { color: navy; }
 a:hover { color: red; }
 h1 { font-size: 150%; margin: 0; padding: .8em 1em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: #eee; }
 h2 { font-size: 150%; margin: 0 0 20px -18px; padding: .8em 1em; border-bottom: 1px solid #000; color: #000; font-weight: normal; background: #ddf; }
-h3 { font-weight: normal; font-size: 130%; margin: .8em 0; }
+h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; }
 form { margin: 0; }
-table { margin: 1em 20px .8em 0; border: 0; border-top: 1px solid #999; border-left: 1px solid #999; font-size: 90%; }
-td, th { margin-bottom: 1em; border: 0; border-right: 1px solid #999; border-bottom: 1px solid #999; padding: .2em .3em; }
+table { margin: 1em 20px 0 0; border: 0; border-top: 1px solid #999; border-left: 1px solid #999; font-size: 90%; }
+td, th { border: 0; border-right: 1px solid #999; border-bottom: 1px solid #999; padding: .2em .3em; }
 th { background: #eee; text-align: left; }
 thead th { text-align: center; }
 thead td, thead th { background: #ddf; }
-fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: 0 .5em .5em 0; border: 1px solid #999; }
-p { margin: 0 20px 1em 0; }
+fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8em .5em 0 0; border: 1px solid #999; }
+p { margin: .8em 20px 0 0; }
 img { vertical-align: middle; border: 0; }
 td img { max-width: 200px; max-height: 200px; }
 code { background: #eee; }
 tr:hover td, tr:hover th { background: #ddf; }
+pre { margin: 1em 0 0; }
 .version { color: #777; font-size: 67%; }
 .js .hidden { display: none; }
 .nowrap td, .nowrap th, td.nowrap { white-space: pre; }
 .wrap td { white-space: normal; }
 .error { color: red; background: #fee; }
+.error b { background: #fff; font-weight: normal; }
 .message { color: green; background: #efe; }
-.error, .message { padding: .5em .8em; margin: 0 20px 1em 0; }
+.error, .message { padding: .5em .8em; margin: 1em 20px 0 0; }
 .char { color: #007F00; }
 .date { color: #7F007F; }
 .enum { color: #007F7F; }
index cadc3aa1618697f06fef244b1aba526fa0792ad1..189d9bfbca6acd0ee56f58d79ef958295fa35082 100644 (file)
@@ -1,7 +1,7 @@
 // Adminer specific functions
 
 /** Load syntax highlighting
-* @param string first three characters of MySQL version
+* @param string first three characters of database system version
 */
 function bodyLoad(version) {
        var jushRoot = '../externals/jush/';
@@ -10,6 +10,7 @@ function bodyLoad(version) {
        script.onload = function () {
                if (window.jush) { // IE runs in case of an error too
                        jush.create_links = ' target="_blank"';
+                       jush.urls.pgsql[0] = 'http://www.postgresql.org/docs/' + version + '/static/$key';
                        jush.urls.sql[0] = 'http://dev.mysql.com/doc/refman/' + version + '/en/$key';
                        jush.urls.sqlset[0] = jush.urls.sql[0];
                        jush.urls.sqlstatus[0] = jush.urls.sql[0];
@@ -26,8 +27,6 @@ function bodyLoad(version) {
        document.body.appendChild(script);
 }
 
-
-
 /** Get value of select
 * @param HTMLSelectElement
 * @return string
index ef958a094832138ff434516c1ed92d7cff12ad88..ab736cafd0ca9b3d2216b63a1f265197c2f4f971 100644 (file)
Binary files a/adminer/static/favicon.ico and b/adminer/static/favicon.ico differ
index fa7de73ea10da2a632677f521e1dc606e5b856d7..3ff592c3d86af7d97248bdd11b9d329a0bedab4d 100644 (file)
@@ -27,7 +27,7 @@ function cookie(assign, days, params) {
 function verifyVersion() {
        cookie('adminer_version=0', 1);
        var script = document.createElement('script');
-       script.src = 'https://adminer.svn.sourceforge.net/svnroot/adminer/trunk/version.js';
+       script.src = 'https://www.adminer.org/version.php';
        document.body.appendChild(script);
 }
 
@@ -83,6 +83,21 @@ function tableClick(event) {
        el.onclick && el.onclick();
 }
 
+/** Set HTML code of an element
+* @param string
+* @param string undefined to set parentNode to &nbsp;
+*/
+function setHtml(id, html) {
+       var el = document.getElementById(id);
+       if (el) {
+               if (html == undefined) {
+                       el.parentNode.innerHTML = '&nbsp;';
+               } else {
+                       el.innerHTML = html;
+               }
+       }
+}
+
 
 
 /** Add row in select fieldset
index 9f2f9afba718397baf4931fe5f225062a25f0da9..6f039c1e9fcc478a37acaff8228c6e04a5c71280 100644 (file)
@@ -6,21 +6,21 @@ if (!$fields) {
 }
 $table_status = ($fields ? table_status($TABLE) : array());
 
-page_header(($fields && !isset($table_status["Rows"]) ? lang('View') : lang('Table')) . ": " . h($TABLE), $error);
+page_header(($fields && $table_status["Engine"] == "VIEW" ? lang('View') : lang('Table')) . ": " . h($TABLE), $error);
 $adminer->selectLinks($table_status);
 
 if ($fields) {
        echo "<table cellspacing='0'>\n";
-       echo "<thead><tr><th>" . lang('Column') . "<td>" . lang('Type') . "<td>" . lang('Comment') . "</thead>\n";
+       echo "<thead><tr><th>" . lang('Column') . "<td>" . lang('Type') . (support("comment") ? "<td>" . lang('Comment') : "") . "</thead>\n";
        foreach ($fields as $field) {
                echo "<tr" . odd() . "><th>" . h($field["field"]);
                echo "<td>" . h($field["full_type"]) . ($field["null"] ? " <i>NULL</i>" : "") . ($field["auto_increment"] ? " <i>" . lang('Auto Increment') . "</i>" : "");
-               echo "<td>" . nbsp($field["comment"]);
+               echo (support("comment") ? "<td>" . nbsp($field["comment"]) : "");
                echo "\n";
        }
        echo "</table>\n";
        
-       if (isset($table_status["Rows"])) {
+       if ($table_status["Engine"] != "VIEW") {
                echo "<h3>" . lang('Indexes') . "</h3>\n";
                $indexes = indexes($TABLE);
                if ($indexes) {
@@ -37,7 +37,7 @@ if ($fields) {
                }
                echo '<p><a href="' . h(ME) . 'indexes=' . urlencode($TABLE) . '">' . lang('Alter indexes') . "</a>\n";
                
-               if ($table_status["Engine"] == "InnoDB") {
+               if (fk_support($table_status)) {
                        echo "<h3>" . lang('Foreign keys') . "</h3>\n";
                        $foreign_keys = foreign_keys($TABLE);
                        if ($foreign_keys) {
@@ -52,16 +52,18 @@ if ($fields) {
                                }
                                echo "</table>\n";
                        }
-                       echo '<p><a href="' . h(ME) . 'foreign=' . urlencode($TABLE) . '">' . lang('Add foreign key') . "</a>\n";
+                       if ($driver != "sqlite") {
+                               echo '<p><a href="' . h(ME) . 'foreign=' . urlencode($TABLE) . '">' . lang('Add foreign key') . "</a>\n";
+                       }
                }
                
-               if ($connection->server_info >= 5) {
+               if (support("trigger")) {
                        echo "<h3>" . lang('Triggers') . "</h3>\n";
-                       $result = $connection->query("SHOW TRIGGERS LIKE " . $connection->quote(addcslashes($TABLE, "%_")));
-                       if ($result->num_rows) {
+                       $triggers = triggers($TABLE);
+                       if ($triggers) {
                                echo "<table cellspacing='0'>\n";
-                               while ($row = $result->fetch_assoc()) {
-                                       echo "<tr valign='top'><td>$row[Timing]<td>$row[Event]<th>" . h($row["Trigger"]) . "<td><a href='" . h(ME . 'trigger=' . urlencode($TABLE) . '&name=' . urlencode($row["Trigger"])) . "'>" . lang('Alter') . "</a>\n";
+                               foreach ($triggers as $key => $val) {
+                                       echo "<tr valign='top'><td>$val[0]<td>$val[1]<th>" . h($key) . "<td><a href='" . h(ME . 'trigger=' . urlencode($TABLE) . '&name=' . urlencode($key)) . "'>" . lang('Alter') . "</a>\n";
                                }
                                echo "</table>\n";
                        }
index 784eb7564988360b32e1b369ca7e414926a21370..8612b1a905e76f30fe0b1c4c90ee4b83c46a3f93 100644 (file)
@@ -6,8 +6,8 @@ $trigger_event = array("INSERT", "UPDATE", "DELETE");
 $dropped = false;
 if ($_POST && !$error && in_array($_POST["Timing"], $trigger_time) && in_array($_POST["Event"], $trigger_event)) {
        $dropped = drop_create(
-               "DROP TRIGGER " . idf_escape($_GET["name"]),
-               "CREATE TRIGGER " . idf_escape($_POST["Trigger"]) . " $_POST[Timing] $_POST[Event] ON " . idf_escape($TABLE) . " FOR EACH ROW\n$_POST[Statement]",
+               "DROP TRIGGER " . idf_escape($_GET["name"]) . ($driver == "pgsql" ? " ON " . idf_escape($TABLE) : ""),
+               "CREATE TRIGGER " . idf_escape($_POST["Trigger"]) . " $_POST[Timing] $_POST[Event] ON " . idf_escape($TABLE) . " FOR EACH ROW\n$_POST[Statement]", //! FOR EACH STATEMENT
                ME . "table=" . urlencode($TABLE),
                lang('Trigger has been dropped.'),
                lang('Trigger has been altered.'),
@@ -22,8 +22,7 @@ $row = array("Trigger" => $TABLE . "_bi");
 if ($_POST) {
        $row = $_POST;
 } elseif ($_GET["name"] != "") {
-       $result = $connection->query("SHOW TRIGGERS WHERE `Trigger` = " . $connection->quote($_GET["name"]));
-       $row = $result->fetch_assoc();
+       $row = trigger($_GET["name"]);
 }
 ?>
 
index 4a8684128f4ae8ccc5ec6f908bd13b47a7deb60d..850dd8fa360228bd37a76c58c7b04248c7d6c6fe 100644 (file)
@@ -107,7 +107,7 @@ if ($_POST) {
        $row = $_POST;
        $grants = $new_grants;
 } else {
-       $row = $_GET + array("host" => $connection->result($connection->query("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', -1)"))); // create user on the same domain by default
+       $row = $_GET + array("host" => $connection->result("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', -1)")); // create user on the same domain by default
        $row["pass"] = $old_pass;
        $row["hashed"] = true;
        $grants[""] = true;
index b78448d5852a345758d150b6161f0b1424840a46..95c805fea9080bf5fb26b9c3c7d6606e069ef8aa 100644 (file)
@@ -5,7 +5,7 @@ if ($_POST && !$error) {
        $dropped = drop_create(
                "DROP VIEW " . idf_escape($TABLE),
                "CREATE VIEW " . idf_escape($_POST["name"]) . " AS\n$_POST[select]",
-               substr(ME, 0, -1),
+               ($_POST["drop"] ? substr(ME, 0, -1) : ME . "table=" . urlencode($_POST["name"])),
                lang('View has been dropped.'),
                lang('View has been altered.'),
                lang('View has been created.'),
index 99b6a254f23a4f1fc91689c905fc0cb4dd1f3353..66e66ad470f2ebd637ff9aa1e29c635913e38125 100644 (file)
@@ -1,3 +1,13 @@
+Adminer 3.0.0-dev:
+Drivers for MS SQL, SQLite, PostgreSQL
+Show number of tables in server overview
+Allow concurrent logins on the same server
+Operator LIKE %%
+Remember export parameters in cookie
+Allow semicolon as CSV separator
+Defer table information in database overview to JavaScript (performance)
+Big tables optimizations (performance)
+
 Adminer 2.3.2 (released 2010-04-21):
 Fix COUNT(*) link
 Fix Save and continue edit
index cf990436e5e0c56b5bffea0330842c850f12a7ff..230dfe797712830673f21cc717ef3158185118b9 100644 (file)
@@ -138,7 +138,7 @@ function php_shrink($input) {
                        } elseif ($token[0] === T_VARIABLE && !isset($special_variables[$token[1]])) {
                                $token[1] = '$' . $short_variables[$token[1]];
                        }
-                       if (isset($set[substr($output, -1)]) || isset($set[$token[1]{0}])) {
+                       if (isset($set[substr($output, -1)]) || isset($set[$token[1][0]])) {
                                $space = '';
                        }
                        $output .= $space . $token[1];
@@ -157,10 +157,16 @@ function compile_file($match) {
        return call_user_func($match[2], file_get_contents(dirname(__FILE__) . "/$project/$match[1]"));
 }
 
+$DRIVER = "";
+if (file_exists(dirname(__FILE__) . "/adminer/drivers/" . $_SERVER["argv"][1] . ".inc.php")) {
+       $DRIVER = $_SERVER["argv"][1];
+       array_shift($_SERVER["argv"]);
+}
+
 unset($_COOKIE["adminer_lang"]);
 $_SESSION["lang"] = $_SERVER["argv"][1]; // Adminer functions read language from session
+include dirname(__FILE__) . "/adminer/include/lang.inc.php";
 if (isset($_SESSION["lang"])) {
-       include dirname(__FILE__) . "/adminer/include/lang.inc.php";
        if (isset($_SERVER["argv"][2]) || !isset($langs[$_SESSION["lang"]])) {
                echo "Usage: php compile.php [lang]\nPurpose: Compile adminer[-lang].php and editor[-lang].php.\n";
                exit(1);
@@ -168,11 +174,44 @@ if (isset($_SESSION["lang"])) {
        include dirname(__FILE__) . "/adminer/lang/$_SESSION[lang].inc.php";
 }
 
+// check function definition in drivers
+$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"]);
+foreach (glob(dirname(__FILE__) . "/adminer/drivers/" . ($DRIVER ? $DRIVER : "*") . ".inc.php") as $filename) {
+       if ($filename != "mysql.inc.php") {
+               $file = file_get_contents($filename);
+               foreach ($functions as $val) {
+                       if (!strpos($file, "$val(")) {
+                               echo "Missing $val in $filename\n";
+                       }
+               }
+       }
+}
+
 foreach (array("adminer", "editor") as $project) {
        $lang_ids = array(); // global variable simplifies usage in a callback function
        $file = file_get_contents(dirname(__FILE__) . "/$project/index.php");
+       if ($DRIVER && $DRIVER != "mysql") {
+               $_GET[$DRIVER] = true; // to load the driver
+               include_once dirname(__FILE__) . "/adminer/drivers/$DRIVER.inc.php";
+               foreach (array("view", "event", "privileges", "user", "processlist", "variables", "trigger") as $feature) {
+                       if (!support($feature)) {
+                               $file = str_replace("} elseif (isset(\$_GET[\"$feature\"])) {\n\tinclude \"./$feature.inc.php\";\n", "", $file);
+                       }
+               }
+               if (!support("routine")) {
+                       $file = str_replace("} elseif (isset(\$_GET[\"procedure\"])) {\n\tinclude \"./procedure.inc.php\";\n", "", $file);
+                       $file = str_replace("} elseif (isset(\$_GET[\"call\"])) {\n\tinclude \"./call.inc.php\";\n", "", $file);
+                       $file = str_replace("if (isset(\$_GET[\"callf\"])) {\n\t\$_GET[\"call\"] = \$_GET[\"callf\"];\n}\nif (isset(\$_GET[\"function\"])) {\n\t\$_GET[\"procedure\"] = \$_GET[\"function\"];\n}\n", "", $file);
+               }
+       }
        $file = preg_replace_callback('~\\b(include|require) "([^"]*)";~', 'put_file', $file);
        $file = str_replace('include "../adminer/include/coverage.inc.php";', '', $file);
+       if ($DRIVER) {
+               $file = preg_replace('(include "../adminer/drivers/(?!' . preg_quote($DRIVER) . ').*\\s*)', '', $file);
+       }
        $file = preg_replace_callback('~\\b(include|require) "([^"]*)";~', 'put_file', $file); // bootstrap.inc.php
        $file = preg_replace_callback("~lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])~s", 'lang_ids', $file);
        $file = preg_replace_callback('~\\b(include|require) "([^"]*\\$LANG.inc.php)";~', 'put_file_lang', $file);
@@ -187,11 +226,11 @@ foreach (array("adminer", "editor") as $project) {
        $replace = 'h(preg_replace("~\\\\\\\\?.*~", "", $_SERVER["REQUEST_URI"])) . "?file=\\1&amp;version=' . $VERSION;
        $file = preg_replace('~\\.\\./adminer/static/(default\\.css|functions\\.js|favicon\\.ico)~', '<?php echo ' . $replace . '"; ?>', $file);
        $file = preg_replace('~\\.\\./adminer/static/([^\'"]*)~', '" . ' . $replace, $file);
-       $file = str_replace("../externals/jush/", "https://jush.svn.sourceforge.net/svnroot/jush/trunk/", $file); // mixed-content warning if Adminer runs on HTTPS and external files on HTTP
+       $file = str_replace("../externals/jush/", "https://www.adminer.org/static/", $file);
        $file = preg_replace("~<\\?php\\s*\\?>\n?|\\?>\n?<\\?php~", '', $file);
        $file = php_shrink($file);
 
-       $filename = $project . ($_SESSION["lang"] ? "-$_SESSION[lang]" : "") . ".php"; // "$project-$VERSION"
+       $filename = $project . ($DRIVER ? "-$DRIVER" : "") . ($_SESSION["lang"] ? "-$_SESSION[lang]" : "") . ".php"; // . "-$VERSION"
        fwrite(fopen($filename, "w"), $file); // file_put_contents() since PHP 5
-       echo "$filename created.\n";
+       echo "$filename created (" . strlen($file) . " B).\n";
 }
index 487b150eb8917485d3ac937bcddf1382d7ccc347..8dc4421a1ddf9788f1e89e1b3c8c014cdc875eb2 100644 (file)
@@ -13,7 +13,7 @@ function xhtml_open_tags($s) {
        $return = array();
        preg_match_all('~<([^>]+)~', $s, $matches);
        foreach ($matches[1] as $val) {
-               if ($val{0} == "/") {
+               if ($val[0] == "/") {
                        array_pop($return);
                } elseif (substr($val, -1) != "/") {
                        $return[] = $val;
index ce13ce9bbb5870088c62730ede7df01773f2a7f5..43abfbdabd3342c253c6f626bcfc190e2ddf8bf0 100644 (file)
@@ -1,9 +1,9 @@
 <?php
-page_header(lang('Server'), "", null);
+page_header(lang('Server'), "", false);
 
 ?>
 <form action=""><p>
-<?php echo SID_FORM; ?>
+<?php hidden_fields_get(); ?>
 <input name="where[0][val]" value="<?php echo h($_GET["where"][0]["val"]); ?>">
 <input type="submit" value="<?php echo lang('Search'); ?>" />
 </form>
index 798729306be1e26b501763e737df16cc66fd6ed7..f6aad529c23b51171898b212cd4b5c6fd41d37b4 100644 (file)
@@ -1,12 +1,14 @@
 <?php
 class Adminer {
        var $operators = array("<=", ">=");
-       var $values = array(); // protected
+       var $_values = array();
        
        function name() {
                return lang('Editor');
        }
        
+       //! driver
+       
        function credentials() {
                return array(); // default INI settings
        }
@@ -19,15 +21,15 @@ class Adminer {
                global $connection;
                $dbs = get_databases(false);
                return (!$dbs
-                       ? $connection->result($connection->query("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', 1)")) // username without the database list
+                       ? $connection->result("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', 1)") // username without the database list
                        : $dbs[(information_schema($dbs[0]) ? 1 : 0)] // first available database
                );
        }
        
-       function loginForm($username) {
+       function loginForm() {
                ?>
 <table cellspacing="0">
-<tr><th><?php echo lang('Username'); ?><td><input type="hidden" name="server" value=""><input name="username" value="<?php echo h($username); ?>">
+<tr><th><?php echo lang('Username'); ?><td><input type="hidden" name="driver" value="server"><input type="hidden" name="server" value=""><input name="username" value="<?php echo h($_GET["username"]);  ?>">
 <tr><th><?php echo lang('Password'); ?><td><input type="password" name="password">
 </table>
 <?php
@@ -59,13 +61,13 @@ class Adminer {
        function backwardKeys($table, $tableName) {
                global $connection;
                $return = array();
-               if ($connection->server_info >= 5) { //! requires MySQL 5
-                       $result = $connection->query("SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME
+               $result = $connection->query("SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME
 FROM information_schema.KEY_COLUMN_USAGE
 WHERE TABLE_SCHEMA = " . $connection->quote($this->database()) . "
 AND REFERENCED_TABLE_SCHEMA = " . $connection->quote($this->database()) . "
 AND REFERENCED_TABLE_NAME = " . $connection->quote($table) . "
 ORDER BY ORDINAL_POSITION");
+               if ($result) { //! requires MySQL 5
                        while ($row = $result->fetch_assoc()) {
                                $return[$row["TABLE_NAME"]]["keys"][$row["CONSTRAINT_NAME"]][$row["COLUMN_NAME"]] = $row["REFERENCED_COLUMN_NAME"];
                        }
@@ -93,7 +95,7 @@ ORDER BY ORDINAL_POSITION");
                                        foreach ($cols as $column => $val) {
                                                $link .= where_link($i++, $column, $row[$val]);
                                        }
-                                       echo "<a href='" . h($link) . "'>$backwardKey[name]</a>";
+                                       echo "<a href='" . h($link) . "'>" . h($backwardKey["name"]) . "</a>";
                                        $link = ME . 'edit=' . urlencode($table);
                                        foreach ($cols as $column => $val) {
                                                $link .= "&set" . urlencode("[" . bracket_escape($column) . "]") . "=" . urlencode($row[$val]);
@@ -119,7 +121,6 @@ ORDER BY ORDINAL_POSITION");
        }
        
        function rowDescriptions($rows, $foreignKeys) {
-               global $connection;
                $return = $rows;
                foreach ($rows[0] as $key => $val) {
                        foreach ((array) $foreignKeys[$key] as $foreignKey) {
@@ -133,7 +134,7 @@ ORDER BY ORDINAL_POSITION");
                                                        $ids[$row[$key]] = exact_value($row[$key]);
                                                }
                                                // uses constant number of queries to get the descriptions, join would be complex, multiple queries would be slow
-                                               $descriptions = $this->values[$foreignKey["table"]];
+                                               $descriptions = $this->_values[$foreignKey["table"]];
                                                if (!$descriptions) {
                                                        $descriptions = get_key_vals("SELECT $id, $name FROM " . idf_escape($foreignKey["table"]) . " WHERE $id IN (" . implode(", ", $ids) . ")");
                                                }
@@ -153,7 +154,7 @@ ORDER BY ORDINAL_POSITION");
        
        function selectVal($val, $link, $field) {
                $return = ($val == "<i>NULL</i>" ? "&nbsp;" : $val);
-               if (ereg('blob|binary', $field["type"]) && !is_utf8($val)) {
+               if (ereg('binary|blob|bytea', $field["type"]) && !is_utf8($val)) {
                        $return = lang('%d byte(s)', strlen($val));
                        if (ereg("^(GIF|\xFF\xD8\xFF|\x89\x50\x4E\x47\x0D\x0A\x1A\x0A)", $val)) { // GIF|JPG|PNG, getimagetype() works with filename
                                $return = "<img src='$link' alt='$return'>";
@@ -181,39 +182,47 @@ ORDER BY ORDINAL_POSITION");
        }
        
        function selectColumnsPrint($select, $columns) {
-               //! allow grouping functions by indexes
+               // can allow grouping functions by indexes
        }
        
        function selectSearchPrint($where, $columns, $indexes) {
-               //! foreign keys
+               $where = (array) $_GET["where"];
                echo '<fieldset><legend>' . lang('Search') . "</legend><div>\n";
                $keys = array();
-               foreach ((array) $_GET["where"] as $key => $val) {
+               foreach ($where as $key => $val) {
                        $keys[$val["col"]] = $key;
                }
-               $i = -1;
+               $i = 0;
+               foreach (fields($_GET["select"]) as $name => $field) {
+                       if (ereg("enum", $field["type"])) { //! set - uses 1 << $i and FIND_IN_SET()
+                               $desc = $columns[$name];
+                               $key = $keys[$name];
+                               $i--;
+                               echo "<div>" . h($desc) . "<input type='hidden' name='where[$i][col]' value='" . h($name) . "'>:";
+                               enum_input("checkbox", "where[$i][val][]", $field, (array) $where[$key]["val"]); //! impossible to search for NULL
+                               echo "</div>\n";
+                               unset($columns[$name]);
+                       }
+               }
                foreach ($columns as $name => $desc) {
-                       $key = $keys[$name];
-                       $options = $this->foreignKeyOptions($_GET["select"], $name);
+                       $options = $this->_foreignKeyOptions($_GET["select"], $name);
                        if ($options) {
-                               echo "<div>" . h($desc) . "<input type='hidden' name='where[$i][col]' value='" . h($name) . "'><input type='hidden' name='where[$i][op]' value='='>: <select name='where[$i][val]'>" . optionlist($options, $_GET["where"][$key]["val"], true) . "</select></div>\n";
+                               $key = $keys[$name];
                                $i--;
+                               echo "<div>" . h($desc) . "<input type='hidden' name='where[$i][col]' value='" . h($name) . "'><input type='hidden' name='where[$i][op]' value='='>: <select name='where[$i][val]'>" . optionlist($options, $where[$key]["val"], true) . "</select></div>\n";
                                unset($columns[$name]);
-                               if (isset($key)) {
-                                       unset($_GET["where"][$key]);
-                               }
                        }
                }
                $i = 0;
-               foreach ((array) $_GET["where"] as $val) {
-                       if ("$val[col]$val[val]" != "") {
-                               echo "<div><select name='where[$i][col]'><option value=''>" . lang('(anywhere)') . optionlist($columns, $val["col"], true) . "</select>";
+               foreach ($where as $val) {
+                       if ($columns[$val["col"]] && "$val[col]$val[val]" != "") {
+                               echo "<div><select name='where[$i][col]'><option value=''>(" . lang('anywhere') . ")" . optionlist($columns, $val["col"], true) . "</select>";
                                echo html_select("where[$i][op]", array(-1 => "") + $this->operators, $val["op"]);
                                echo "<input name='where[$i][val]' value='" . h($val["val"]) . "'></div>\n";
                                $i++;
                        }
                }
-               echo "<div><select name='where[$i][col]' onchange='selectAddRow(this);'><option value=''>" . lang('(anywhere)') . optionlist($columns, null, true) . "</select>";
+               echo "<div><select name='where[$i][col]' onchange='selectAddRow(this);'><option value=''>(" . lang('anywhere') . ")" . optionlist($columns, null, true) . "</select>";
                echo html_select("where[$i][op]", array(-1 => "") + $this->operators);
                echo "<input name='where[$i][val]'></div>\n";
                echo "</div></fieldset>\n";
@@ -260,7 +269,7 @@ ORDER BY ORDINAL_POSITION");
                        echo lang('Subject') . ": <input name='email_subject' value='" . h($_POST["email_subject"]) . "'>\n";
                        echo "<p><textarea name='email_message' rows='15' cols='75'>" . h($_POST["email_message"] . ($_POST["email_append"] ? '{$' . "$_POST[email_addition]}" : "")) . "</textarea><br>\n";
                        echo html_select("email_addition", $columns, $_POST["email_addition"]) . "<input type='submit' name='email_append' value='" . lang('Insert') . "'>\n"; //! JavaScript
-                       echo "<p><input type='file' name='email_files[]' onchange=\"var el = this.cloneNode(true); el.value = ''; this.parentNode.appendChild(el); this.onchange = function () { };\">";
+                       echo "<p>" . lang('Attachments') . ": <input type='file' name='email_files[]' onchange=\"var el = this.cloneNode(true); el.value = ''; this.parentNode.appendChild(el); this.onchange = function () { };\">";
                        echo "<p>" . (count($emailFields) == 1 ? '<input type="hidden" name="email_field" value="' . h(key($emailFields)) . '">' : html_select("email_field", $emailFields));
                        echo "<input type='submit' name='email' value='" . lang('Send') . "' onclick=\"return this.form['delete'].onclick();\">\n";
                        echo "</div></fieldset>\n";
@@ -273,15 +282,21 @@ ORDER BY ORDINAL_POSITION");
        
        function selectSearchProcess($fields, $indexes) {
                $return = array();
-               foreach ((array) $_GET["where"] as $key => $val) {
-                       $col = $val["col"];
-                       if (($key < 0 ? "" : $col) . $val["val"] != "") {
+               foreach ((array) $_GET["where"] as $key => $where) {
+                       $col = $where["col"];
+                       $op = $where["op"];
+                       $val = $where["val"];
+                       if (($key < 0 ? "" : $col) . $val != "") {
                                $conds = array();
                                foreach (($col != "" ? array($col => $fields[$col]) : $fields) as $name => $field) {
-                                       if ($col != "" || is_numeric($val["val"]) || !ereg('int|float|double|decimal', $field["type"])) {
-                                               $text_type = ereg('char|text|enum|set', $field["type"]);
-                                               $value = $this->processInput($field, ($text_type && ereg('^[^%]+$', $val["val"]) ? "%$val[val]%" : $val["val"]));
-                                               $conds[] = idf_escape($name) . ($value == "NULL" ? " IS" . ($val["op"] == ">=" ? " NOT" : "") : (in_array($val["op"], $this->operators) ? " $val[op]" : ($val["op"] != "=" && $text_type ? " LIKE" : " ="))) . " $value"; //! can issue "Illegal mix of collations" for columns in other character sets - solve by CONVERT($name using utf8)
+                                       if ($col != "" || is_numeric($val) || !ereg('int|float|double|decimal', $field["type"])) {
+                                               if ($col != "" && $field["type"] == "enum") {
+                                                       $conds[] = idf_escape($name) . " IN (" . implode(", ", array_map('intval', $val)) . ")";
+                                               } else {
+                                                       $text_type = ereg('char|text|enum|set', $field["type"]);
+                                                       $value = $this->processInput($field, ($text_type && ereg('^[^%]+$', $val) ? "%$val%" : $val));
+                                                       $conds[] = idf_escape($name) . ($value == "NULL" ? " IS" . ($op == ">=" ? " NOT" : "") : (in_array($op, $this->operators) ? " $op" : ($op != "=" && $text_type ? " LIKE" : " ="))) . " $value"; //! can issue "Illegal mix of collations" for columns in other character sets - solve by CONVERT($name using utf8)
+                                               }
                                        }
                                }
                                $return[] = ($conds ? "(" . implode(" OR ", $conds) . ")" : "0");
@@ -332,8 +347,7 @@ ORDER BY ORDINAL_POSITION");
                        if ($_POST["all"] || $_POST["check"]) {
                                $field = idf_escape($_POST["email_field"]);
                                $subject = $_POST["email_subject"];
-                               $eol = (strncasecmp(PHP_OS, "win", 3) ? "\n" : "\r\n");
-                               $message = str_replace("\n", $eol, wordwrap(str_replace("\r", "", "$_POST[email_message]\n")));
+                               $message = $_POST["email_message"];
                                preg_match_all('~\\{\\$([a-z0-9_]+)\\}~i', "$subject.$message", $matches); // allows {$name} in subject or message
                                $result = $connection->query("SELECT DISTINCT $field" . ($matches[1] ? ", " . implode(", ", array_map('idf_escape', array_unique($matches[1]))) : "") . " FROM " . idf_escape($_GET["select"])
                                        . " WHERE $field IS NOT NULL AND $field != ''"
@@ -344,37 +358,14 @@ ORDER BY ORDINAL_POSITION");
                                while ($row = $result->fetch_assoc()) {
                                        $rows[] = $row;
                                }
-                               $boundary = uniqid("boundary");
-                               $attachments = "";
-                               $email_files = $_FILES["email_files"];
-                               foreach ($email_files["error"] as $key => $val) {
-                                       if (!$val) {
-                                               $attachments .= "--$boundary$eol"
-                                                       . "Content-Type: " . str_replace("\n", "", $email_files["type"][$key]) . $eol
-                                                       . "Content-Disposition: attachment; filename=\"" . preg_replace('~["\\n]~', '', $email_files["name"][$key]) . "\"$eol"
-                                                       . "Content-Transfer-Encoding: base64$eol"
-                                                       . $eol . chunk_split(base64_encode(file_get_contents($email_files["tmp_name"][$key])), 76, $eol) . $eol
-                                               ;
-                                       }
-                               }
-                               $beginning = "";
-                               $headers = "Content-Type: text/plain; charset=utf-8$eol" . "Content-Transfer-Encoding: 8bit";
-                               if ($attachments) {
-                                       $attachments .= "--$boundary--$eol";
-                                       $beginning = "--$boundary$eol$headers$eol$eol";
-                                       $headers = "Content-Type: multipart/mixed; boundary=\"$boundary\"";
-                               }
-                               $headers .= $eol . "MIME-Version: 1.0$eol" . "X-Mailer: Adminer Editor"
-                                       . ($_POST["email_from"] ? $eol . "From: " . str_replace("\n", "", $_POST["email_from"]) : "") //! should escape display name
-                               ;
                                $fields = fields($_GET["select"]);
                                foreach ($this->rowDescriptions($rows, $foreignKeys) as $row) {
-                                       $replace = array();
+                                       $replace = array('{\\' => '{'); // allow literal {$name}
                                        foreach ($matches[1] as $val) {
-                                               $replace['{$' . "$val}"] = $this->editVal($row[$val], $fields[$val]); //! allow literal {$name}
+                                               $replace['{$' . "$val}"] = $this->editVal($row[$val], $fields[$val]);
                                        }
                                        $email = $row[$_POST["email_field"]];
-                                       if (is_email($email) && mail($email, email_header(strtr($subject, $replace)), $beginning . strtr($message, $replace) . $attachments, $headers)) {
+                                       if (is_email($email) && send_email($email, strtr($subject, $replace), strtr($message, $replace), $_POST["email_from"], $_FILES["email_files"])) {
                                                $sent++;
                                        }
                                }
@@ -391,6 +382,7 @@ ORDER BY ORDINAL_POSITION");
        
        function editFunctions($field) {
                $return = array("" => ($field["null"] || $field["auto_increment"] || $field["full_type"] == "tinyint(1)" ? "" : "*"));
+               //! respect driver
                if (ereg('date|time', $field["type"])) {
                        $return[] = "now";
                }
@@ -404,7 +396,7 @@ ORDER BY ORDINAL_POSITION");
                if ($field["type"] == "enum") {
                        return ($field["null"] ? "<input type='radio'$attrs value=''" . ($value || isset($_GET["select"]) ? "" : " checked") . ">" : "");
                }
-               $options = $this->foreignKeyOptions($table, $field["field"]);
+               $options = $this->_foreignKeyOptions($table, $field["field"]);
                if ($options) {
                        return "<select$attrs>" . optionlist($options, $value, true) . "</select>";
                }
@@ -429,20 +421,22 @@ ORDER BY ORDINAL_POSITION");
                $return = $connection->quote($return);
                if (!ereg('varchar|text', $field["type"]) && $field["full_type"] != "tinyint(1)" && $value == "") {
                        $return = "NULL";
+               } elseif (ereg('^(md5|sha1)$', $function)) {
+                       $return = "$function($return)";
                }
                return $return;
        }
        
-       function dumpOutput($select) {
+       function dumpOutput($select, $value = "") {
                return "";
        }
        
-       function dumpFormat($select) {
-               return "CSV";
+       function dumpFormat($select, $value = "") {
+               return html_select("format", array('csv' => 'CSV,', 'csv;' => 'CSV;'), $value, $select);
        }
        
        function navigation($missing) {
-               global $VERSION;
+               global $VERSION, $token;
                ?>
 <h1>
 <a href="http://www.adminer.org/" id="h1"><?php echo $this->name(); ?></a>
@@ -454,7 +448,7 @@ ORDER BY ORDINAL_POSITION");
                        ?>
 <form action="" method="post">
 <p class="logout">
-<input type="hidden" name="token" value="<?php echo $_SESSION["tokens"][$_GET["server"]]; ?>">
+<input type="hidden" name="token" value="<?php echo $token; ?>">
 <input type="submit" name="logout" value="<?php echo lang('Logout'); ?>">
 </p>
 </form>
@@ -480,27 +474,25 @@ ORDER BY ORDINAL_POSITION");
                }
        }
        
-       function foreignKeyOptions($table, $column) { // protected
+       function _foreignKeyOptions($table, $column) {
                global $connection;
+               $table_status = table_status($table);
                $foreignKeys = column_foreign_keys($table);
                foreach ((array) $foreignKeys[$column] as $foreignKey) {
                        if (count($foreignKey["source"]) == 1) {
                                $id = idf_escape($foreignKey["target"][0]);
                                $name = $this->rowDescription($foreignKey["table"]);
                                if ($name != "") {
-                                       $return = &$this->values[$foreignKey["table"]];
+                                       $return = &$this->_values[$foreignKey["table"]];
                                        if (!isset($return)) {
-                                               $return = array("" => "") + get_key_vals("SELECT $id, $name FROM " . idf_escape($foreignKey["table"]) . " ORDER BY 2 LIMIT 1001");
-                                               if (count($return) > 1001) {
-                                                       $return = array();
-                                               }
+                                               $return = ($table_status["Rows"] > 1000 ? array() : array("" => "") + get_key_vals("SELECT $id, $name FROM " . idf_escape($foreignKey["table"]) . " ORDER BY 2"));
                                        }
                                        return $return;
                                }
                        }
                }
        }
-       
+
 }
 
 $adminer = (function_exists('adminer_object') ? adminer_object() : new Adminer);
index a1d73585b1441cb8d77f304f5fd285d99b48a39f..58fedbb5fae33f521be9a4437c788e8c91f79f47 100644 (file)
@@ -8,16 +8,38 @@ function email_header($header) {
        return "=?UTF-8?B?" . base64_encode($header) . "?="; //! split long lines
 }
 
-/** Get keys from first column and values from second
+/** Send e-mail in UTF-8
 * @param string
-* @return array
+* @param string
+* @param string
+* @param string
+* @param array
+* @return 
 */
-function get_key_vals($query) {
-       global $connection;
-       $return = array();
-       $result = $connection->query($query);
-       while ($row = $result->fetch_row()) {
-               $return[$row[0]] = $row[1];
+function send_email($email, $subject, $message, $from = "", $files = array()) {
+       $eol = (strncasecmp(PHP_OS, "win", 3) ? "\n" : "\r\n"); // PHP_EOL available since PHP 4.3.10 and 5.0.2
+       $message = str_replace("\n", $eol, wordwrap(str_replace("\r", "", "$message\n")));
+       $boundary = uniqid("boundary");
+       $attachments = "";
+       foreach ($files["error"] as $key => $val) {
+               if (!$val) {
+                       $attachments .= "--$boundary$eol"
+                               . "Content-Type: " . str_replace("\n", "", $files["type"][$key]) . $eol
+                               . "Content-Disposition: attachment; filename=\"" . preg_replace('~["\\n]~', '', $files["name"][$key]) . "\"$eol"
+                               . "Content-Transfer-Encoding: base64$eol$eol"
+                               . chunk_split(base64_encode(file_get_contents($files["tmp_name"][$key])), 76, $eol) . $eol
+                       ;
+               }
+       }
+       $beginning = "";
+       $headers = "Content-Type: text/plain; charset=utf-8$eol" . "Content-Transfer-Encoding: 8bit";
+       if ($attachments) {
+               $attachments .= "--$boundary--$eol";
+               $beginning = "--$boundary$eol$headers$eol$eol";
+               $headers = "Content-Type: multipart/mixed; boundary=\"$boundary\"";
        }
-       return $return;
+       $headers .= $eol . "MIME-Version: 1.0$eol" . "X-Mailer: Adminer Editor"
+               . ($from ? $eol . "From: " . str_replace("\n", "", $from) : "") //! should escape display name
+       ;
+       return mail($email, email_header($subject), $beginning . $message . $attachments, $headers);
 }
index 112f201879cc0792c6f931d77a16d7d1040cd5e4..ef6d351431daed9ef0ae8623ef4c6bcfa38687c2 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Adminer Editor - Compact MySQL editor
+/** Adminer Editor - Compact database editor
 * @link http://www.adminer.org/
 * @author Jakub Vrana, http://php.vrana.cz/
 * @copyright 2009 Jakub Vrana
@@ -7,6 +7,7 @@
 */
 
 include "../adminer/include/bootstrap.inc.php";
+$drivers[DRIVER] = lang('Login');
 
 if (isset($_GET["select"]) && ($_POST["edit"] || $_POST["clone"]) && !$_POST["save"]) {
        $_GET["edit"] = $_GET["select"];
index 8e1ae8a8fb94c163b457ba3d7ec81ea71427c82b..0510518e90e229db69bd915664bb4b8891f73e40 100644 (file)
--- a/lang.php
+++ b/lang.php
@@ -14,6 +14,7 @@ $messages_all = array();
 foreach (array_merge(
        glob(dirname(__FILE__) . "/adminer/*.php"),
        glob(dirname(__FILE__) . "/adminer/include/*.php"),
+       glob(dirname(__FILE__) . "/adminer/drivers/*.php"),
        glob(dirname(__FILE__) . "/editor/*.php"),
        glob(dirname(__FILE__) . "/editor/include/*.php")
 ) as $filename) {
index f238acca35e06b347119de2eeb967e9ccba018cd..384fce968b608dbf4475e2bbb77712d442433392 100644 (file)
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/coverage.php?coverage=0</td>
+       <td>coverage.php?coverage=0</td>
        <td></td>
 </tr>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?lang=en&amp;username=ODBC</td>
+       <td>adminer/?username=ODBC&amp;lang=en</td>
        <td></td>
 </tr>
 <tr>
index 5ff2dc9833c3f4a5ec637410e3578cf09cbfdc57..6ed566de7e2dd32c44628fb397cf46eea5f9fe89 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/</td>
+       <td>adminer/?username=ODBC</td>
        <td></td>
 </tr>
 <tr>
index 2fad3a6864ca387815958646b3436d09a36e1f6e..2dfa38f5ca7ab438724ac76de3f7cac0640081c0 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;select=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;select=albums</td>
        <td></td>
 </tr>
 <tr>
index 02411c53c6d7d045f637d304835ff1c96395efb0..89ec709c695b3ca35c8ff10538c885c03ba6d0d5 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;select=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;select=albums</td>
        <td></td>
 </tr>
 <tr>
index dfa19da9bb4c410787f5d96f1b61c8f199988291..0e1be973e25a7b4791746f047e1745494b43b239 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;edit=albums&amp;where%5Bid%5D=2</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;edit=albums&amp;where%5Bid%5D=2</td>
        <td></td>
 </tr>
 <tr>
index 058f9ab10fd2cc9d6d34525b5a3c1dc571ce1747..2f9ff8f3617dc3fd27d9aa7d24c2c5b361cb4fff 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;select=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;select=albums</td>
        <td></td>
 </tr>
 <tr>
index cc95bc2e3251208f9cdb7bc007ee40fa06313eb1..f93d1c63b95fda2062cf1ea0c7b08541ab2392e3 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;select=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;select=albums</td>
        <td></td>
 </tr>
 <tr>
index ec655068b225c694e92dd0572b57b8c7765753a2..577a74f54ce6cd0ee61bba3a9bbdc286bb8580a4 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?user=</td>
+       <td>adminer/?username=ODBC&amp;user=</td>
        <td></td>
 </tr>
 <tr>
index e9c58348061a528d7ec4a93d035bed7d64544e5b..5f08ec332b0406edb92c7fa4ea8acb2a528e0990 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?processlist=</td>
+       <td>adminer/?username=ODBC&amp;processlist=</td>
        <td></td>
 </tr>
 <tr>
index 36f4010b0ae93ba60ebb79c96e3cf303b0654496..cf85f7ee163075f6bd6d48985fefc55759e55455 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;dump=</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;dump=</td>
        <td></td>
 </tr>
 <tr>
index c66da6a632518c0a3e598ed0152def96e5cf9fea..d4b4994d4de72f6f287087de81f24d99022ad476 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;event=</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;event=</td>
        <td></td>
 </tr>
 <tr>
index 0eccd296e591c5dc18cd033c5521e3e9f39b90c8..3d3bd3e288dd8f7d8266c2788d7c91294aa9732a 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;procedure=</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;procedure=</td>
        <td></td>
 </tr>
 <tr>
index 1620815de6af80403fa1a90beb1ce967a89f998d..22cfbf804ba153c8186a060443f626bb0ae9853a 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test</td>
        <td></td>
 </tr>
 <tr>
index b19ab1eb43fae6f1053455f3339c7c39d886f855..b4661cf2d98f3c3245c333a0905ca6c2d1a9fb75 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;table=interprets</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;table=interprets</td>
        <td></td>
 </tr>
 <tr>
index e043a4db608deb924f47822e454b3a5e8dfa1521..7f76283552b88adffc41c6fdfec34f423f66b125 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?variables=</td>
+       <td>adminer/?username=ODBC&amp;variables=</td>
        <td></td>
 </tr>
 <tr>
index e6645957150099dd25c1209503efb2cce6aaafde..6c1142abb6b2f352d530ffe8350980269fa08482 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?sql=</td>
+       <td>adminer/?username=ODBC&amp;sql=</td>
        <td></td>
 </tr>
 <tr>
index 46f6328c67e773ca9b3fe9f27c92f28756717825..86948fc7f480e16c7bf878fd0d5735fb7454d7a5 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/editor/example.php?lang=en&amp;username=admin</td>
+       <td>editor/example.php?lang=en&amp;username=admin</td>
        <td></td>
 </tr>
 <tr>
index 995acec3a9e34428d3415f77dc2445cc3f9d181e..f0b0ccafd3733258e1c06f8e49c11784506366af 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;select=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;select=albums</td>
        <td></td>
 </tr>
 <tr>
index 72b484782bd3d74c9ff581546c68e126c5afab8c..6db2ca7f71928bb5783389e9ffb4dbdd9db65e3d 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;table=interprets</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;table=interprets</td>
        <td></td>
 </tr>
 <tr>
index ca8437d57ca5fe35d5cec603ca26b8a93e7708c3..5bfe609b81e8f1ee9b4a4908d5a90df31d8b34bd 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;table=interprets&amp;lang=en</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;table=interprets&amp;lang=en</td>
        <td></td>
 </tr>
 <tr>
index 1992e3a9d8cbbdb20c1f1fb760318788e15ef435..2b87ea8321540fbdcf4cc11aee60a65b1f032ada 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;table=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;table=albums</td>
        <td></td>
 </tr>
 <tr>
index d983a90007a987dafc7015691f47ed3d576e3023..98e501b272ddb222b8d88959efce3d2344e758a5 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;table=interprets</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;table=interprets</td>
        <td></td>
 </tr>
 <tr>
index 8fae68f3d393a845b8b0a840f195fa2197027252..328873a28f0a3f3e76456b7eeab51e33529e1d1f 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;trigger=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;trigger=albums</td>
        <td></td>
 </tr>
 <tr>
index ce8e4a445d9e31268e9cb562cd381f049fb51351..37623f29e4a4016986636623b03d168950774149 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;view=</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;view=</td>
        <td></td>
 </tr>
 <tr>
index 60eebf6c5b6936b75c8cdb00f1af3364824fcc22..8def6caad8d384ccead76937076560f3be8670ac 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;edit=interprets</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;edit=interprets</td>
        <td></td>
 </tr>
 <tr>
@@ -33,7 +33,7 @@
 </tr>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/?db=adminer_test&amp;edit=albums</td>
+       <td>adminer/?username=ODBC&amp;db=adminer_test&amp;edit=albums</td>
        <td></td>
 </tr>
 <tr>
index 1e1480cda2c8585f54db4e224b30fdaf8b0150e8..e4623742631e32514e0a15d8a930c0d40aad7dd0 100644 (file)
@@ -13,7 +13,7 @@
 </thead><tbody>
 <tr>
        <td>open</td>
-       <td>/adminer/adminer/</td>
+       <td>adminer/?username=ODBC</td>
        <td></td>
 </tr>
 <tr>
@@ -38,7 +38,7 @@
 </tr>
 <tr>
        <td>open</td>
-       <td>/adminer/coverage.php</td>
+       <td>coverage.php</td>
        <td></td>
 </tr>
 
index 1a1247598aa5a2d9802954823e958a9d96243a77..6141fbf59b97727c45758c0ae9fd38472af00707 100644 (file)
--- a/todo.txt
+++ b/todo.txt
@@ -2,9 +2,7 @@ MySQL 5 BIT data type
 Transactions in export
 Create view and routine options
 Mass editation of individual rows
-Offer enum and set items in search - whisperer
 Variables editation, especially timezone
-Use event $intervals + microseconds in relative date functions
 Optionally check IP address
 Disable spell checking in SQL textareas - spellcheck="false"
 Accept Tab in SQL textareas, Ctrl+Enter to send form
@@ -12,14 +10,40 @@ Highlight SQL textarea, then display query inside textarea in select - may use e
 Blob download and image display in edit form (important for Editor with hidden fields in select)
 Add title to Logout, edit (in select) and select (in menu) in style "hever"
 Shift-click in checkboxes to select range
-? LIKE %% operator
+Export by GET parameters
+Only first part of big BZ2 export is readable
 ? Column and table names auto-completition in SQL textarea
-? Aliasing of built-in functions can save 7 KB, function minification can save 7 KB, substitution of repetitive $a["a"] can save 4 KB, substitution of $_GET and friends can save 2 KB, JS packer can save 1 KB, not enclosing HTML attribute values can save 1.2 KB, replacing \\n by \n can save .3 KB
+? Aliasing of built-in functions can save 7 KB, function minification can save 7 KB, substitution of repetitive $a["a"] can save 4 KB, substitution of $_GET and friends can save 2 KB, aliasing of $connection->query, $connection->result and $connection->quote can save ~ 3 KB, JS packer can save 1 KB, not enclosing HTML attribute values can save 1.2 KB, replacing \\n by \n can save .3 KB
 ? Branch binary_compile: LZW compression of translations can save 30 KB, LZW compression of all texts can save 11 KB, remove of base64_decode() + using chars 127-255 in minification can save 1 KB
 ? AJAX editing - select page has all data to display edit form
+? MySQL geometry support
+Translations - database(s) have been dropped
 
 Editor:
 JavaScript data validation - columns containing word email, url, ...
 Joining tables - PRIMARY KEY (table, joining)
 Rank, Tree structure
 Add whisperer to fields with foreign key to big table
+
+SQLite:
+CREATE DATABASE - PRAGMA encoding = "UTF-8"
+Detecion of non-existing database
+DROP DATABASE by file operations
+CSV import - ON DUPLICATE KEY UPDATE
+Export - views, triggers
+Delimiter in export and SQL command
+
+PostgreSQL:
+Users - SELECT * FROM pg_user
+ORDER BY COUNT(*)
+Table schema
+Export - http://www.postgresql.org/docs/8.4/static/functions-info.html
+Table status - http://www.postgresql.org/docs/8.4/static/functions-admin.html
+Column rights - http://www.postgresql.org/docs/8.4/static/functions-info.html
+Move table - ALTER TABLE SET SCHEMA
+bool in Editor
+
+MS SQL:
+Rename by sp_rename
+Detection of table collation
+PDO driver
index 86e472f45fbcb436b21ef1d7a8bf490d5fb00840..119a4ecd10bacecccbc5df4ce4bb2db467b68eaf 100644 (file)
@@ -1,4 +1,4 @@
-// downloaded from repository by verifyVersion()
+// downloaded from repository by verifyVersion() before Adminer 3.0.0
 (function () { // cookie function is not defined in older versions
        var date = new Date();
        date.setDate(date.getDate() + 7); // valid for 7 days