\n";
echo "" . lang('Name') . " | " . lang('Schedule') . " | " . lang('Start') . " | " . lang('End') . " |
\n";
while ($row = $result->fetch_assoc()) {
@@ -125,3 +124,34 @@ if ($connection->server_info >= 5.1 && ($result = $connection->query("SHOW EVENT
}
echo '' . lang('Create event') . "\n";
}
+
+page_footer();
+$table_status = table_status();
+if ($table_status) {
+ echo "\n";
+}
+exit; // page_footer() already called
diff --git a/adminer/download.inc.php b/adminer/download.inc.php
index dcf39415..faa28ce4 100644
--- a/adminer/download.inc.php
+++ b/adminer/download.inc.php
@@ -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
index 00000000..98f38e99
--- /dev/null
+++ b/adminer/drivers/mssql.inc.php
@@ -0,0 +1,406 @@
+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
index 00000000..f6006744
--- /dev/null
+++ b/adminer/drivers/mysql.inc.php
@@ -0,0 +1,713 @@
+ "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
index 00000000..a42a21e6
--- /dev/null
+++ b/adminer/drivers/pgsql.inc.php
@@ -0,0 +1,443 @@
+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\\2', $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
index 00000000..48ee0460
--- /dev/null
+++ b/adminer/drivers/sqlite.inc.php
@@ -0,0 +1,479 @@
+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" => "||",
+ )
+ );
+}
diff --git a/adminer/dump.inc.php b/adminer/dump.inc.php
index 12336c56..4f455ac1 100644
--- a/adminer/dump.inc.php
+++ b/adminer/dump.inc.php
@@ -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 "
" . lang('Output') . " | " . $adminer->dumpOutput(0) . "\n"; // token is not needed but checked in bootstrap for all POST data //! read from cookie
-echo " |
---|
" . lang('Format') . " | " . $adminer->dumpFormat(0) . "\n";
-echo " |
---|
" . lang('Database') . " | " . html_select('db_style', $db_style, (DB != "" ? '' : 'CREATE'));
-if ($connection->server_info >= 5) {
- $checked = $_GET["dump"] == "";
- echo checkbox("routines", 1, $checked, lang('Routines'));
- if ($connection->server_info >= 5.1) {
- echo checkbox("events", 1, $checked, lang('Events'));
- }
+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 " |
---|
" . lang('Tables') . " | " . html_select('table_style', $table_style, 'DROP+CREATE');
-echo " |
---|
" . lang('Data') . " | " . html_select('data_style', $data_style, 'INSERT');
+echo " |
---|
" . lang('Output') . " | " . $adminer->dumpOutput(0, $row["output"]) . "\n";
+echo " |
---|
" . lang('Format') . " | " . $adminer->dumpFormat(0, $row["format"]) . "\n";
+echo " |
---|
" . lang('Database') . " | " . html_select('db_style', $db_style, $row["db_style"]);
+$checked = ($_GET["dump"] == "");
+if (support("routine")) {
+ echo checkbox("routines", 1, $checked, lang('Routines'));
+}
+if (support("event")) {
+ echo checkbox("events", 1, $checked, lang('Events'));
+}
+echo " |
---|
" . lang('Tables') . " | " . html_select('table_style', $table_style, $row["table_style"]);
+echo " |
---|
" . lang('Data') . " | " . html_select('data_style', $data_style, $row["data_style"]);
?>
|
---|
";
}
}
?>
diff --git a/adminer/edit.inc.php b/adminer/edit.inc.php
index f0fb7781..a85587bf 100644
--- a/adminer/edit.inc.php
+++ b/adminer/edit.inc.php
@@ -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);
diff --git a/adminer/foreign.inc.php b/adminer/foreign.inc.php
index bc117a1c..200d3a03 100644
--- a/adminer/foreign.inc.php
+++ b/adminer/foreign.inc.php
@@ -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;
+ }
+}
?>
diff --git a/editor/include/adminer.inc.php b/editor/include/adminer.inc.php
index 79872930..f6aad529 100644
--- a/editor/include/adminer.inc.php
+++ b/editor/include/adminer.inc.php
@@ -1,12 +1,14 @@
=");
- 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() {
?>
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 "$backwardKey[name]";
+ echo "" . h($backwardKey["name"]) . "";
$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 == "NULL" ? " " : $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 = " ";
@@ -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 ' |