]> git.joonet.de Git - adminer.git/commitdiff
SimpleDB, Firebird, ClickHouse: Move to plugin
authorJakub Vrana <jakub@vrana.cz>
Mon, 8 Feb 2021 18:56:15 +0000 (19:56 +0100)
committerJakub Vrana <jakub@vrana.cz>
Mon, 8 Feb 2021 18:56:15 +0000 (19:56 +0100)
13 files changed:
adminer/drivers/clickhouse.inc.php [deleted file]
adminer/drivers/firebird.inc.php [deleted file]
adminer/drivers/simpledb.inc.php [deleted file]
adminer/include/bootstrap.inc.php
adminer/include/driver.inc.php
adminer/include/pdo.inc.php
adminer/plugin.php
changes.txt
compile.php
plugins/drivers/README [new file with mode: 0644]
plugins/drivers/clickhouse.inc.php [new file with mode: 0644]
plugins/drivers/firebird.inc.php [new file with mode: 0644]
plugins/drivers/simpledb.inc.php [new file with mode: 0644]

diff --git a/adminer/drivers/clickhouse.inc.php b/adminer/drivers/clickhouse.inc.php
deleted file mode 100644 (file)
index ec5db51..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-<?php
-$drivers["clickhouse"] = "ClickHouse (alpha)";
-
-if (isset($_GET["clickhouse"])) {
-       define("DRIVER", "clickhouse");
-
-       class Min_DB {
-               var $extension = "JSON", $server_info, $errno, $_result, $error, $_url;
-               var $_db = 'default';
-
-               function rootQuery($db, $query) {
-                       @ini_set('track_errors', 1); // @ - may be disabled
-                       $file = @file_get_contents("$this->_url/?database=$db", false, stream_context_create(array('http' => array(
-                               'method' => 'POST',
-                               'content' => $this->isQuerySelectLike($query) ? "$query FORMAT JSONCompact" : $query,
-                               'header' => 'Content-type: application/x-www-form-urlencoded',
-                               'ignore_errors' => 1, // available since PHP 5.2.10
-                       ))));
-
-                       if ($file === false) {
-                               $this->error = $php_errormsg;
-                               return $file;
-                       }
-                       if (!preg_match('~^HTTP/[0-9.]+ 2~i', $http_response_header[0])) {
-                               $this->error = lang('Invalid credentials.') . " $http_response_header[0]";
-                               return false;
-                       }
-                       $return = json_decode($file, true);
-                       if ($return === null) {
-                               if (!$this->isQuerySelectLike($query) && $file === '') {
-                                       return true;
-                               }
-
-                               $this->errno = json_last_error();
-                               if (function_exists('json_last_error_msg')) {
-                                       $this->error = json_last_error_msg();
-                               } else {
-                                       $constants = get_defined_constants(true);
-                                       foreach ($constants['json'] as $name => $value) {
-                                               if ($value == $this->errno && preg_match('~^JSON_ERROR_~', $name)) {
-                                                       $this->error = $name;
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
-                       return new Min_Result($return);
-               }
-
-               function isQuerySelectLike($query) {
-                       return (bool) preg_match('~^(select|show)~i', $query);
-               }
-
-               function query($query) {
-                       return $this->rootQuery($this->_db, $query);
-               }
-
-               function connect($server, $username, $password) {
-                       preg_match('~^(https?://)?(.*)~', $server, $match);
-                       $this->_url = ($match[1] ? $match[1] : "http://") . "$username:$password@$match[2]";
-                       $return = $this->query('SELECT 1');
-                       return (bool) $return;
-               }
-
-               function select_db($database) {
-                       $this->_db = $database;
-                       return true;
-               }
-
-               function quote($string) {
-                       return "'" . addcslashes($string, "\\'") . "'";
-               }
-
-               function multi_query($query) {
-                       return $this->_result = $this->query($query);
-               }
-
-               function store_result() {
-                       return $this->_result;
-               }
-
-               function next_result() {
-                       return false;
-               }
-
-               function result($query, $field = 0) {
-                       $result = $this->query($query);
-                       return $result['data'];
-               }
-       }
-
-       class Min_Result {
-               var $num_rows, $_rows, $columns, $meta, $_offset = 0;
-
-               function __construct($result) {
-                       $this->num_rows = $result['rows'];
-                       $this->_rows = $result['data'];
-                       $this->meta = $result['meta'];
-                       $this->columns = array_column($this->meta, 'name');
-                       reset($this->_rows);
-               }
-
-               function fetch_assoc() {
-                       $row = current($this->_rows);
-                       next($this->_rows);
-                       return $row === false ? false : array_combine($this->columns, $row);
-               }
-
-               function fetch_row() {
-                       $row = current($this->_rows);
-                       next($this->_rows);
-                       return $row;
-               }
-
-               function fetch_field() {
-                       $column = $this->_offset++;
-                       $return = new stdClass;
-                       if ($column < count($this->columns)) {
-                               $return->name = $this->meta[$column]['name'];
-                               $return->orgname = $return->name;
-                               $return->type = $this->meta[$column]['type'];
-                       }
-                       return $return;
-               }
-       }
-
-
-       class Min_Driver extends Min_SQL {
-               function delete($table, $queryWhere, $limit = 0) {
-                       if ($queryWhere === '') {
-                               $queryWhere = 'WHERE 1=1';
-                       }
-                       return queries("ALTER TABLE " . table($table) . " DELETE $queryWhere");
-               }
-
-               function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
-                       $values = array();
-                       foreach ($set as $key => $val) {
-                               $values[] = "$key = $val";
-                       }
-                       $query = $separator . implode(",$separator", $values);
-                       return queries("ALTER TABLE " . table($table) . " UPDATE $query$queryWhere");
-               }
-       }
-
-       function idf_escape($idf) {
-               return "`" . str_replace("`", "``", $idf) . "`";
-       }
-
-       function table($idf) {
-               return idf_escape($idf);
-       }
-
-       function explain($connection, $query) {
-               return '';
-       }
-
-       function found_rows($table_status, $where) {
-               $rows = get_vals("SELECT COUNT(*) FROM " . idf_escape($table_status["Name"]) . ($where ? " WHERE " . implode(" AND ", $where) : ""));
-               return empty($rows) ? false : $rows[0];
-       }
-
-       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
-               $alter = $order = array();
-               foreach ($fields as $field) {
-                       if ($field[1][2] === " NULL") {
-                               $field[1][1] = " Nullable({$field[1][1]})";
-                       } elseif ($field[1][2] === ' NOT NULL') {
-                               $field[1][2] = '';
-                       }
-
-                       if ($field[1][3]) {
-                               $field[1][3] = '';
-                       }
-
-                       $alter[] = ($field[1]
-                               ? ($table != "" ? ($field[0] != "" ? "MODIFY COLUMN " : "ADD COLUMN ") : " ") . implode($field[1])
-                               : "DROP COLUMN " . idf_escape($field[0])
-                       );
-
-                       $order[] = $field[1][0];
-               }
-
-               $alter = array_merge($alter, $foreign);
-               $status = ($engine ? " ENGINE " . $engine : "");
-               if ($table == "") {
-                       return queries("CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)$status$partitioning" . ' ORDER BY (' . implode(',', $order) . ')');
-               }
-               if ($table != $name) {
-                       $result = queries("RENAME TABLE " . table($table) . " TO " . table($name));
-                       if ($alter) {
-                               $table = $name;
-                       } else {
-                               return $result;
-                       }
-               }
-               if ($status) {
-                       $alter[] = ltrim($status);
-               }
-               return ($alter || $partitioning ? queries("ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter) . $partitioning) : true);
-       }
-
-       function truncate_tables($tables) {
-               return apply_queries("TRUNCATE TABLE", $tables);
-       }
-
-       function drop_views($views) {
-               return drop_tables($views);
-       }
-
-       function drop_tables($tables) {
-               return apply_queries("DROP TABLE", $tables);
-       }
-
-       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($flush) {
-               global $connection;
-               $result = get_rows('SHOW DATABASES');
-
-               $return = array();
-               foreach ($result as $row) {
-                       $return[] = $row['name'];
-               }
-               sort($return);
-               return $return;
-       }
-
-       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
-               return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" . ($offset ? ", $offset" : "") : "");
-       }
-
-       function limit1($table, $query, $where, $separator = "\n") {
-               return limit($query, $where, 1, 0, $separator);
-       }
-
-       function db_collation($db, $collations) {
-       }
-
-       function engines() {
-               return array('MergeTree');
-       }
-
-       function logged_user() {
-               global $adminer;
-               $credentials = $adminer->credentials();
-               return $credentials[1];
-       }
-
-       function tables_list() {
-               $result = get_rows('SHOW TABLES');
-               $return = array();
-               foreach ($result as $row) {
-                       $return[$row['name']] = 'table';
-               }
-               ksort($return);
-               return $return;
-       }
-
-       function count_tables($databases) {
-               return array();
-       }
-
-       function table_status($name = "", $fast = false) {
-               global $connection;
-               $return = array();
-               $tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q($connection->_db));
-               foreach ($tables as $table) {
-                       $return[$table['name']] = array(
-                               'Name' => $table['name'],
-                               'Engine' => $table['engine'],
-                       );
-                       if ($name === $table['name']) {
-                               return $return[$table['name']];
-                       }
-               }
-               return $return;
-       }
-
-       function is_view($table_status) {
-               return false;
-       }
-
-       function fk_support($table_status) {
-               return false;
-       }
-
-       function convert_field($field) {
-       }
-
-       function unconvert_field($field, $return) {
-               if (in_array($field['type'], array("Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Float32", "Float64"))) {
-                       return "to$field[type]($return)";
-               }
-               return $return;
-       }
-
-       function fields($table) {
-               $return = array();
-               $result = get_rows("SELECT name, type, default_expression FROM system.columns WHERE " . idf_escape('table') . " = " . q($table));
-               foreach ($result as $row) {
-                       $type = trim($row['type']);
-                       $nullable = strpos($type, 'Nullable(') === 0;
-                       $return[trim($row['name'])] = array(
-                               "field" => trim($row['name']),
-                               "full_type" => $type,
-                               "type" => $type,
-                               "default" => trim($row['default_expression']),
-                               "null" => $nullable,
-                               "auto_increment" => '0',
-                               "privileges" => array("insert" => 1, "select" => 1, "update" => 0),
-                       );
-               }
-
-               return $return;
-       }
-
-       function indexes($table, $connection2 = null) {
-               return array();
-       }
-
-       function foreign_keys($table) {
-               return array();
-       }
-
-       function collations() {
-               return array();
-       }
-
-       function information_schema($db) {
-               return false;
-       }
-
-       function error() {
-               global $connection;
-               return h($connection->error);
-       }
-
-       function types() {
-               return array();
-       }
-
-       function schemas() {
-               return array();
-       }
-
-       function get_schema() {
-               return "";
-       }
-
-       function set_schema($schema) {
-               return true;
-       }
-
-       function auto_increment() {
-               return '';
-       }
-
-       function last_id() {
-               return 0; // ClickHouse doesn't have it
-       }
-
-       function support($feature) {
-               return preg_match("~^(columns|sql|status|table|drop_col)$~", $feature);
-       }
-
-       function driver_config() {
-               $types = array();
-               $structured_types = array();
-               foreach (array( //! arrays
-                       lang('Numbers') => array("Int8" => 3, "Int16" => 5, "Int32" => 10, "Int64" => 19, "UInt8" => 3, "UInt16" => 5, "UInt32" => 10, "UInt64" => 20, "Float32" => 7, "Float64" => 16, 'Decimal' => 38, 'Decimal32' => 9, 'Decimal64' => 18, 'Decimal128' => 38),
-                       lang('Date and time') => array("Date" => 13, "DateTime" => 20),
-                       lang('Strings') => array("String" => 0),
-                       lang('Binary') => array("FixedString" => 0),
-               ) as $key => $val) {
-                       $types += $val;
-                       $structured_types[$key] = array_keys($val);
-               }
-               return array(
-                       'jush' => "clickhouse",
-                       'types' => $types,
-                       'structured_types' => $structured_types,
-                       'unsigned' => array(),
-                       'operators' => array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"),
-                       'functions' => array(),
-                       'grouping' => array("avg", "count", "count distinct", "max", "min", "sum"),
-                       'edit_functions' => array(),
-               );
-       }
-}
diff --git a/adminer/drivers/firebird.inc.php b/adminer/drivers/firebird.inc.php
deleted file mode 100644 (file)
index 7e62930..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-/**
-* @author Steve Krämer
-*/
-
-$drivers['firebird'] = 'Firebird (alpha)';
-       
-if (isset($_GET["firebird"])) {
-       define("DRIVER", "firebird");
-
-       if (extension_loaded("interbase") ) {
-               class Min_DB {
-                       var
-                               $extension = "Firebird",
-                               $server_info,
-                               $affected_rows,
-                               $errno,
-                               $error,
-                               $_link, $_result
-                       ;
-
-                       function connect($server, $username, $password) {
-                               $this->_link = ibase_connect($server, $username, $password); 
-                               if ($this->_link) {
-                                       $url_parts = explode(':', $server);
-                                       $this->service_link = ibase_service_attach($url_parts[0], $username, $password);
-                                       $this->server_info = ibase_server_info($this->service_link, IBASE_SVC_SERVER_VERSION);
-                               } else {
-                                       $this->errno = ibase_errcode();
-                                       $this->error = ibase_errmsg();
-                               }
-                               return (bool) $this->_link;
-                       }
-
-                       function quote($string) {
-                               return "'" . str_replace("'", "''", $string) . "'";
-                       }
-
-                       function select_db($database) {
-                               return ($database == "domain");
-                       }
-
-                       function query($query, $unbuffered = false) {
-                               $result = ibase_query($query, $this->_link);
-                               if (!$result) {
-                                       $this->errno = ibase_errcode();
-                                       $this->error = ibase_errmsg();
-                                       return false;
-                               }
-                               $this->error = "";
-                               if ($result === true) {
-                                       $this->affected_rows = ibase_affected_rows($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 false;
-                       }
-
-                       function result($query, $field = 0) {
-                               $result = $this->query($query);
-                               if (!$result || !$result->num_rows) {
-                                       return false;
-                               }
-                               $row = $result->fetch_row();
-                               return $row[$field];
-                       }
-               }
-
-               class Min_Result {
-                       var $num_rows, $_result, $_offset = 0;
-
-                       function __construct($result) {
-                               $this->_result = $result;
-                               // $this->num_rows = ibase_num_rows($result);
-                       }
-
-                       function fetch_assoc() {
-                               return ibase_fetch_assoc($this->_result);
-                       }
-
-                       function fetch_row() {
-                               return ibase_fetch_row($this->_result);
-                       }
-
-                       function fetch_field() {
-                               $field = ibase_field_info($this->_result, $this->_offset++);
-                               return (object) array(
-                                       'name' => $field['name'],
-                                       'orgname' => $field['name'],
-                                       'type' => $field['type'],
-                                       'charsetnr' => $field['length'],
-                               );
-                       }
-
-                       function __destruct() {
-                               ibase_free_result($this->_result);
-                       }
-               }
-
-       }
-       
-       
-       
-       class Min_Driver extends Min_SQL {
-       }
-
-
-
-       function idf_escape($idf) {
-               return '"' . str_replace('"', '""', $idf) . '"';
-       }
-
-       function table($idf) {
-               return idf_escape($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($flush) {
-               return array("domain");
-       }
-
-       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
-               $return = ''; 
-               $return .= ($limit !== null ? $separator . "FIRST $limit" . ($offset ? " SKIP $offset" : "") : "");
-               $return .= " $query$where";
-               return $return;
-       }
-
-       function limit1($table, $query, $where, $separator = "\n") {
-               return limit($query, $where, 1, 0, $separator);
-       }
-
-       function db_collation($db, $collations) {
-       }
-
-       function engines() {
-               return array();
-       }
-
-       function logged_user() {
-               global $adminer;
-               $credentials = $adminer->credentials();
-               return $credentials[1];
-       }
-
-       function tables_list() {
-               global $connection;
-               $query = 'SELECT RDB$RELATION_NAME FROM rdb$relations WHERE rdb$system_flag = 0';
-               $result = ibase_query($connection->_link, $query);
-               $return = array();
-               while ($row = ibase_fetch_assoc($result)) {
-                               $return[$row['RDB$RELATION_NAME']] = 'table';
-               }
-               ksort($return); 
-               return $return;
-       }
-
-       function count_tables($databases) {
-               return array();
-       }
-
-       function table_status($name = "", $fast = false) {
-               global $connection;
-               $return = array();
-               $data = tables_list();
-               foreach ($data as $index => $val) {
-                       $index = trim($index);
-                       $return[$index] = array(
-                               'Name' => $index,
-                               'Engine' => 'standard',
-                       );
-                       if ($name == $index) {
-                               return $return[$index];
-                       }
-               }
-               return $return;
-       }
-
-       function is_view($table_status) {
-               return false;
-       }
-
-       function fk_support($table_status) {
-               return preg_match('~InnoDB|IBMDB2I~i', $table_status["Engine"]);
-       }
-
-       function fields($table) {
-               global $connection;
-               $return = array();
-               $query = 'SELECT r.RDB$FIELD_NAME AS field_name,
-r.RDB$DESCRIPTION AS field_description,
-r.RDB$DEFAULT_VALUE AS field_default_value,
-r.RDB$NULL_FLAG AS field_not_null_constraint,
-f.RDB$FIELD_LENGTH AS field_length,
-f.RDB$FIELD_PRECISION AS field_precision,
-f.RDB$FIELD_SCALE AS field_scale,
-CASE f.RDB$FIELD_TYPE
-WHEN 261 THEN \'BLOB\'
-WHEN 14 THEN \'CHAR\'
-WHEN 40 THEN \'CSTRING\'
-WHEN 11 THEN \'D_FLOAT\'
-WHEN 27 THEN \'DOUBLE\'
-WHEN 10 THEN \'FLOAT\'
-WHEN 16 THEN \'INT64\'
-WHEN 8 THEN \'INTEGER\'
-WHEN 9 THEN \'QUAD\'
-WHEN 7 THEN \'SMALLINT\'
-WHEN 12 THEN \'DATE\'
-WHEN 13 THEN \'TIME\'
-WHEN 35 THEN \'TIMESTAMP\'
-WHEN 37 THEN \'VARCHAR\'
-ELSE \'UNKNOWN\'
-END AS field_type,
-f.RDB$FIELD_SUB_TYPE AS field_subtype,
-coll.RDB$COLLATION_NAME AS field_collation,
-cset.RDB$CHARACTER_SET_NAME AS field_charset
-FROM RDB$RELATION_FIELDS r
-LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME
-LEFT JOIN RDB$COLLATIONS coll ON f.RDB$COLLATION_ID = coll.RDB$COLLATION_ID
-LEFT JOIN RDB$CHARACTER_SETS cset ON f.RDB$CHARACTER_SET_ID = cset.RDB$CHARACTER_SET_ID
-WHERE r.RDB$RELATION_NAME = ' . q($table) . '
-ORDER BY r.RDB$FIELD_POSITION';
-               $result = ibase_query($connection->_link, $query);
-               while ($row = ibase_fetch_assoc($result)) {
-                       $return[trim($row['FIELD_NAME'])] = array(
-                               "field" => trim($row["FIELD_NAME"]),
-                               "full_type" => trim($row["FIELD_TYPE"]),
-                               "type" => trim($row["FIELD_SUB_TYPE"]),
-                               "default" => trim($row['FIELD_DEFAULT_VALUE']),
-                               "null" => (trim($row["FIELD_NOT_NULL_CONSTRAINT"]) == "YES"),
-                               "auto_increment" => '0',
-                               "collation" => trim($row["FIELD_COLLATION"]),
-                               "privileges" => array("insert" => 1, "select" => 1, "update" => 1),
-                               "comment" => trim($row["FIELD_DESCRIPTION"]),
-                       );
-               }
-               return $return;
-       }
-
-       function indexes($table, $connection2 = null) {
-               $return = array();
-               /*
-               $query = 'SELECT RDB$INDEX_SEGMENTS.RDB$FIELD_NAME AS field_name,
-RDB$INDICES.RDB$DESCRIPTION AS description,
-(RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION + 1) AS field_position
-FROM RDB$INDEX_SEGMENTS
-LEFT JOIN RDB$INDICES ON RDB$INDICES.RDB$INDEX_NAME = RDB$INDEX_SEGMENTS.RDB$INDEX_NAME
-LEFT JOIN RDB$RELATION_CONSTRAINTS ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDEX_SEGMENTS.RDB$INDEX_NAME
-WHERE UPPER(RDB$INDICES.RDB$RELATION_NAME) = ' . q($table) . '
-// AND UPPER(RDB$INDICES.RDB$INDEX_NAME) = \'TEST2_FIELD5_IDX\'
-AND RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE IS NULL
-ORDER BY RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION';
-               */
-               return $return;
-       }
-
-       function foreign_keys($table) {
-               return array();
-       }
-
-       function collations() {
-               return array();
-       }
-
-       function information_schema($db) {
-               return false;
-       }
-
-       function error() {
-               global $connection;
-               return h($connection->error);
-       }
-
-       function types() {
-               return array();
-       }
-
-       function schemas() {
-               return array();
-       }
-
-       function get_schema() {
-               return "";
-       }
-
-       function set_schema($schema) {
-               return true;
-       }
-
-       function support($feature) {
-               return preg_match("~^(columns|sql|status|table)$~", $feature);
-       }
-
-       function driver_config() {
-               return array(
-                       'possible_drivers' => array("interbase"),
-                       'jush' => "firebird",
-                       'operators' => array("="),
-                       'functions' => array(),
-                       'grouping' => array(),
-                       'edit_functions' => array(),
-               );
-       }
-}
diff --git a/adminer/drivers/simpledb.inc.php b/adminer/drivers/simpledb.inc.php
deleted file mode 100644 (file)
index 4dce229..0000000
+++ /dev/null
@@ -1,488 +0,0 @@
-<?php
-$drivers["simpledb"] = "SimpleDB";
-
-if (isset($_GET["simpledb"])) {
-       define("DRIVER", "simpledb");
-
-       if (class_exists('SimpleXMLElement') && ini_bool('allow_url_fopen')) {
-               class Min_DB {
-                       var $extension = "SimpleXML", $server_info = '2009-04-15', $error, $timeout, $next, $affected_rows, $_result;
-
-                       function select_db($database) {
-                               return ($database == "domain");
-                       }
-
-                       function query($query, $unbuffered = false) {
-                               $params = array('SelectExpression' => $query, 'ConsistentRead' => 'true');
-                               if ($this->next) {
-                                       $params['NextToken'] = $this->next;
-                               }
-                               $result = sdb_request_all('Select', 'Item', $params, $this->timeout); //! respect $unbuffered
-                               $this->timeout = 0;
-                               if ($result === false) {
-                                       return $result;
-                               }
-                               if (preg_match('~^\s*SELECT\s+COUNT\(~i', $query)) {
-                                       $sum = 0;
-                                       foreach ($result as $item) {
-                                               $sum += $item->Attribute->Value;
-                                       }
-                                       $result = array((object) array('Attribute' => array((object) array(
-                                               'Name' => 'Count',
-                                               'Value' => $sum,
-                                       ))));
-                               }
-                               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 false;
-                       }
-
-                       function quote($string) {
-                               return "'" . str_replace("'", "''", $string) . "'";
-                       }
-
-               }
-
-               class Min_Result {
-                       var $num_rows, $_rows = array(), $_offset = 0;
-
-                       function __construct($result) {
-                               foreach ($result as $item) {
-                                       $row = array();
-                                       if ($item->Name != '') { // SELECT COUNT(*)
-                                               $row['itemName()'] = (string) $item->Name;
-                                       }
-                                       foreach ($item->Attribute as $attribute) {
-                                               $name = $this->_processValue($attribute->Name);
-                                               $value = $this->_processValue($attribute->Value);
-                                               if (isset($row[$name])) {
-                                                       $row[$name] = (array) $row[$name];
-                                                       $row[$name][] = $value;
-                                               } else {
-                                                       $row[$name] = $value;
-                                               }
-                                       }
-                                       $this->_rows[] = $row;
-                                       foreach ($row as $key => $val) {
-                                               if (!isset($this->_rows[0][$key])) {
-                                                       $this->_rows[0][$key] = null;
-                                               }
-                                       }
-                               }
-                               $this->num_rows = count($this->_rows);
-                       }
-
-                       function _processValue($element) {
-                               return (is_object($element) && $element['encoding'] == 'base64' ? base64_decode($element) : (string) $element);
-                       }
-
-                       function fetch_assoc() {
-                               $row = current($this->_rows);
-                               if (!$row) {
-                                       return $row;
-                               }
-                               $return = array();
-                               foreach ($this->_rows[0] as $key => $val) {
-                                       $return[$key] = $row[$key];
-                               }
-                               next($this->_rows);
-                               return $return;
-                       }
-
-                       function fetch_row() {
-                               $return = $this->fetch_assoc();
-                               if (!$return) {
-                                       return $return;
-                               }
-                               return array_values($return);
-                       }
-
-                       function fetch_field() {
-                               $keys = array_keys($this->_rows[0]);
-                               return (object) array('name' => $keys[$this->_offset++]);
-                       }
-
-               }
-       }
-
-
-
-       class Min_Driver extends Min_SQL {
-               public $primary = "itemName()";
-
-               function _chunkRequest($ids, $action, $params, $expand = array()) {
-                       global $connection;
-                       foreach (array_chunk($ids, 25) as $chunk) {
-                               $params2 = $params;
-                               foreach ($chunk as $i => $id) {
-                                       $params2["Item.$i.ItemName"] = $id;
-                                       foreach ($expand as $key => $val) {
-                                               $params2["Item.$i.$key"] = $val;
-                                       }
-                               }
-                               if (!sdb_request($action, $params2)) {
-                                       return false;
-                               }
-                       }
-                       $connection->affected_rows = count($ids);
-                       return true;
-               }
-
-               function _extractIds($table, $queryWhere, $limit) {
-                       $return = array();
-                       if (preg_match_all("~itemName\(\) = (('[^']*+')+)~", $queryWhere, $matches)) {
-                               $return = array_map('idf_unescape', $matches[1]);
-                       } else {
-                               foreach (sdb_request_all('Select', 'Item', array('SelectExpression' => 'SELECT itemName() FROM ' . table($table) . $queryWhere . ($limit ? " LIMIT 1" : ""))) as $item) {
-                                       $return[] = $item->Name;
-                               }
-                       }
-                       return $return;
-               }
-
-               function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
-                       global $connection;
-                       $connection->next = $_GET["next"];
-                       $return = parent::select($table, $select, $where, $group, $order, $limit, $page, $print);
-                       $connection->next = 0;
-                       return $return;
-               }
-
-               function delete($table, $queryWhere, $limit = 0) {
-                       return $this->_chunkRequest(
-                               $this->_extractIds($table, $queryWhere, $limit),
-                               'BatchDeleteAttributes',
-                               array('DomainName' => $table)
-                       );
-               }
-
-               function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
-                       $delete = array();
-                       $insert = array();
-                       $i = 0;
-                       $ids = $this->_extractIds($table, $queryWhere, $limit);
-                       $id = idf_unescape($set["`itemName()`"]);
-                       unset($set["`itemName()`"]);
-                       foreach ($set as $key => $val) {
-                               $key = idf_unescape($key);
-                               if ($val == "NULL" || ($id != "" && array($id) != $ids)) {
-                                       $delete["Attribute." . count($delete) . ".Name"] = $key;
-                               }
-                               if ($val != "NULL") {
-                                       foreach ((array) $val as $k => $v) {
-                                               $insert["Attribute.$i.Name"] = $key;
-                                               $insert["Attribute.$i.Value"] = (is_array($val) ? $v : idf_unescape($v));
-                                               if (!$k) {
-                                                       $insert["Attribute.$i.Replace"] = "true";
-                                               }
-                                               $i++;
-                                       }
-                               }
-                       }
-                       $params = array('DomainName' => $table);
-                       return (!$insert || $this->_chunkRequest(($id != "" ? array($id) : $ids), 'BatchPutAttributes', $params, $insert))
-                               && (!$delete || $this->_chunkRequest($ids, 'BatchDeleteAttributes', $params, $delete))
-                       ;
-               }
-
-               function insert($table, $set) {
-                       $params = array("DomainName" => $table);
-                       $i = 0;
-                       foreach ($set as $name => $value) {
-                               if ($value != "NULL") {
-                                       $name = idf_unescape($name);
-                                       if ($name == "itemName()") {
-                                               $params["ItemName"] = idf_unescape($value);
-                                       } else {
-                                               foreach ((array) $value as $val) {
-                                                       $params["Attribute.$i.Name"] = $name;
-                                                       $params["Attribute.$i.Value"] = (is_array($value) ? $val : idf_unescape($value));
-                                                       $i++;
-                                               }
-                                       }
-                               }
-                       }
-                       return sdb_request('PutAttributes', $params);
-               }
-
-               function insertUpdate($table, $rows, $primary) {
-                       //! use one batch request
-                       foreach ($rows as $set) {
-                               if (!$this->update($table, $set, "WHERE `itemName()` = " . q($set["`itemName()`"]))) {
-                                       return false;
-                               }
-                       }
-                       return true;
-               }
-
-               function begin() {
-                       return false;
-               }
-
-               function commit() {
-                       return false;
-               }
-
-               function rollback() {
-                       return false;
-               }
-               
-               function slowQuery($query, $timeout) {
-                       $this->_conn->timeout = $timeout;
-                       return $query;
-               }
-
-       }
-
-
-
-       function connect() {
-               global $adminer;
-               list(, , $password) = $adminer->credentials();
-               if ($password != "") {
-                       return lang('Database does not support password.');
-               }
-               return new Min_DB;
-       }
-
-       function support($feature) {
-               return preg_match('~sql~', $feature);
-       }
-
-       function logged_user() {
-               global $adminer;
-               $credentials = $adminer->credentials();
-               return $credentials[1];
-       }
-
-       function get_databases() {
-               return array("domain");
-       }
-
-       function collations() {
-               return array();
-       }
-
-       function db_collation($db, $collations) {
-       }
-
-       function tables_list() {
-               global $connection;
-               $return = array();
-               foreach (sdb_request_all('ListDomains', 'DomainName') as $table) {
-                       $return[(string) $table] = 'table';
-               }
-               if ($connection->error && defined("PAGE_HEADER")) {
-                       echo "<p class='error'>" . error() . "\n";
-               }
-               return $return;
-       }
-
-       function table_status($name = "", $fast = false) {
-               $return = array();
-               foreach (($name != "" ? array($name => true) : tables_list()) as $table => $type) {
-                       $row = array("Name" => $table, "Auto_increment" => "");
-                       if (!$fast) {
-                               $meta = sdb_request('DomainMetadata', array('DomainName' => $table));
-                               if ($meta) {
-                                       foreach (array(
-                                               "Rows" => "ItemCount",
-                                               "Data_length" => "ItemNamesSizeBytes",
-                                               "Index_length" => "AttributeValuesSizeBytes",
-                                               "Data_free" => "AttributeNamesSizeBytes",
-                                       ) as $key => $val) {
-                                               $row[$key] = (string) $meta->$val;
-                                       }
-                               }
-                       }
-                       if ($name != "") {
-                               return $row;
-                       }
-                       $return[$table] = $row;
-               }
-               return $return;
-       }
-
-       function explain($connection, $query) {
-       }
-
-       function error() {
-               global $connection;
-               return h($connection->error);
-       }
-
-       function information_schema() {
-       }
-
-       function is_view($table_status) {
-       }
-
-       function indexes($table, $connection2 = null) {
-               return array(
-                       array("type" => "PRIMARY", "columns" => array("itemName()")),
-               );
-       }
-
-       function fields($table) {
-               return fields_from_edit();
-       }
-
-       function foreign_keys($table) {
-               return array();
-       }
-
-       function table($idf) {
-               return idf_escape($idf);
-       }
-
-       function idf_escape($idf) {
-               return "`" . str_replace("`", "``", $idf) . "`";
-       }
-
-       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
-               return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" : "");
-       }
-
-       function unconvert_field($field, $return) {
-               return $return;
-       }
-
-       function fk_support($table_status) {
-       }
-
-       function engines() {
-               return array();
-       }
-
-       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
-               return ($table == "" && sdb_request('CreateDomain', array('DomainName' => $name)));
-       }
-
-       function drop_tables($tables) {
-               foreach ($tables as $table) {
-                       if (!sdb_request('DeleteDomain', array('DomainName' => $table))) {
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       function count_tables($databases) {
-               foreach ($databases as $db) {
-                       return array($db => count(tables_list()));
-               }
-       }
-
-       function found_rows($table_status, $where) {
-               return ($where ? null : $table_status["Rows"]);
-       }
-
-       function last_id() {
-       }
-
-       function hmac($algo, $data, $key, $raw_output = false) {
-               // can use hash_hmac() since PHP 5.1.2
-               $blocksize = 64;
-               if (strlen($key) > $blocksize) {
-                       $key = pack("H*", $algo($key));
-               }
-               $key = str_pad($key, $blocksize, "\0");
-               $k_ipad = $key ^ str_repeat("\x36", $blocksize);
-               $k_opad = $key ^ str_repeat("\x5C", $blocksize);
-               $return = $algo($k_opad . pack("H*", $algo($k_ipad . $data)));
-               if ($raw_output) {
-                       $return = pack("H*", $return);
-               }
-               return $return;
-       }
-
-       function sdb_request($action, $params = array()) {
-               global $adminer, $connection;
-               list($host, $params['AWSAccessKeyId'], $secret) = $adminer->credentials();
-               $params['Action'] = $action;
-               $params['Timestamp'] = gmdate('Y-m-d\TH:i:s+00:00');
-               $params['Version'] = '2009-04-15';
-               $params['SignatureVersion'] = 2;
-               $params['SignatureMethod'] = 'HmacSHA1';
-               ksort($params);
-               $query = '';
-               foreach ($params as $key => $val) {
-                       $query .= '&' . rawurlencode($key) . '=' . rawurlencode($val);
-               }
-               $query = str_replace('%7E', '~', substr($query, 1));
-               $query .= "&Signature=" . urlencode(base64_encode(hmac('sha1', "POST\n" . preg_replace('~^https?://~', '', $host) . "\n/\n$query", $secret, true)));
-               @ini_set('track_errors', 1); // @ - may be disabled
-               $file = @file_get_contents((preg_match('~^https?://~', $host) ? $host : "http://$host"), false, stream_context_create(array('http' => array(
-                       'method' => 'POST', // may not fit in URL with GET
-                       'content' => $query,
-                       'ignore_errors' => 1, // available since PHP 5.2.10
-               ))));
-               if (!$file) {
-                       $connection->error = $php_errormsg;
-                       return false;
-               }
-               libxml_use_internal_errors(true);
-               $xml = simplexml_load_string($file);
-               if (!$xml) {
-                       $error = libxml_get_last_error();
-                       $connection->error = $error->message;
-                       return false;
-               }
-               if ($xml->Errors) {
-                       $error = $xml->Errors->Error;
-                       $connection->error = "$error->Message ($error->Code)";
-                       return false;
-               }
-               $connection->error = '';
-               $tag = $action . "Result";
-               return ($xml->$tag ? $xml->$tag : true);
-       }
-
-       function sdb_request_all($action, $tag, $params = array(), $timeout = 0) {
-               $return = array();
-               $start = ($timeout ? microtime(true) : 0);
-               $limit = (preg_match('~LIMIT\s+(\d+)\s*$~i', $params['SelectExpression'], $match) ? $match[1] : 0);
-               do {
-                       $xml = sdb_request($action, $params);
-                       if (!$xml) {
-                               break;
-                       }
-                       foreach ($xml->$tag as $element) {
-                               $return[] = $element;
-                       }
-                       if ($limit && count($return) >= $limit) {
-                               $_GET["next"] = $xml->NextToken;
-                               break;
-                       }
-                       if ($timeout && microtime(true) - $start > $timeout) {
-                               return false;
-                       }
-                       $params['NextToken'] = $xml->NextToken;
-                       if ($limit) {
-                               $params['SelectExpression'] = preg_replace('~\d+\s*$~', $limit - count($return), $params['SelectExpression']);
-                       }
-               } while ($xml->NextToken);
-               return $return;
-       }
-
-       function driver_config() {
-               return array(
-                       'possible_drivers' => array("SimpleXML + allow_url_fopen"),
-                       'jush' => "simpledb",
-                       'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "IS NOT NULL"),
-                       'functions' => array(),
-                       'grouping' => array("count"),
-                       'edit_functions' => array(array("json")),
-               );
-       }
-}
index 114b4ca481b2d65825a6ae7c38d9f8b1f5ad27f3..9349d42c8308536cd15dce3ef84936d7cf3cb735 100644 (file)
@@ -80,11 +80,9 @@ include "../adminer/drivers/sqlite.inc.php";
 include "../adminer/drivers/pgsql.inc.php";
 include "../adminer/drivers/oracle.inc.php";
 include "../adminer/drivers/mssql.inc.php";
-include "../adminer/drivers/firebird.inc.php";
-include "../adminer/drivers/simpledb.inc.php";
 include "../adminer/drivers/mongo.inc.php";
 include "../adminer/drivers/elastic.inc.php";
-include "../adminer/drivers/clickhouse.inc.php";
+include "./include/adminer.inc.php";
 include "../adminer/drivers/mysql.inc.php"; // must be included as last driver
 
 $config = driver_config();
@@ -108,7 +106,6 @@ define("ME", preg_replace('~\?.*~', '', relative_uri()) . '?'
 );
 
 include "../adminer/include/version.inc.php";
-include "./include/adminer.inc.php";
 include "../adminer/include/design.inc.php";
 include "../adminer/include/xxtea.inc.php";
 include "../adminer/include/auth.inc.php";
index 893d16677f54e2d549fb3e59730ccc833333e0f1..99582bd4e2b5ce74d2764bcc1cc1bb607d11e685 100644 (file)
@@ -1,4 +1,15 @@
 <?php
+$drivers = array();
+
+/** Add a driver
+* @param string
+* @param string
+* @return null
+*/
+function add_driver($id, $name) {
+       global $drivers;
+       $drivers[$id] = $name;
+}
 
 /*abstract*/ class Min_SQL {
        var $_conn;
index ed7ac49726d77461932b3fec3d504704d450148a..b0f8b62bafb4ac0a9bbc8e3f9fcf6a66bff6ee14 100644 (file)
@@ -100,5 +100,3 @@ if (extension_loaded('pdo')) {
                }
        }
 }
-
-$drivers = array();
index 3fb1b549a30d432ac5c0191da368d1403cb112cb..31071fce4810c1afcd6e0122a899390c9c224a72 100644 (file)
@@ -8,6 +8,9 @@ function adminer_object() {
                include_once $filename;
        }
        
+       // enable extra drivers just by including them
+       //~ include "../plugins/drivers/simpledb.inc.php";
+       
        $plugins = array(
                // specify enabled plugins here
                new AdminerDatabaseHide(array('information_schema')),
index 830f7e88ff45c754ef41148ee5a614b4f5b34420..a5fdebaa5da87314fb5afccc48a283cb89d27e6a 100644 (file)
@@ -9,6 +9,7 @@ PostgreSQL: Create PRIMARY KEY for auto increment columns
 PostgreSQL: Avoid exporting empty sequence last value (bug #768)
 PostgreSQL: Fix multi-parameter functions in default values (bug #736)
 PostgreSQL PDO: Do not select NULL function for false values in edit
+SimpleDB, Firebird, ClickHouse: Move to plugin
 
 Adminer 4.7.9 (released 2021-02-07):
 Fix XSS in browsers which don't encode URL parameters (bug #775, regression from 4.7.0)
index 574fe6efbaffa8ba13d4075d325f0bca31033ba5..eb68525326f75496f50278843f1792d433fcb35b 100755 (executable)
@@ -41,7 +41,7 @@ function lang_ids($match) {
 }
 
 function put_file($match) {
-       global $project, $VERSION;
+       global $project, $VERSION, $driver;
        if (basename($match[2]) == '$LANG.inc.php') {
                return $match[0]; // processed later
        }
@@ -61,6 +61,9 @@ header("Cache-Control: immutable");
                        echo "adminer/file.inc.php: Caching headers placeholder not found\n";
                }
        }
+       if ($driver && dirname($match[2]) == "../adminer/drivers") {
+               $return = preg_replace('~^if \(isset\(\$_GET\["' . $driver . '"]\)\) \{(.*)^}~ms', '\1', $return);
+       }
        if (basename($match[2]) != "lang.inc.php" || !$_SESSION["lang"]) {
                if (basename($match[2]) == "lang.inc.php") {
                        $return = str_replace('function lang($idf, $number = null) {', 'function lang($idf, $number = null) {
diff --git a/plugins/drivers/README b/plugins/drivers/README
new file mode 100644 (file)
index 0000000..ee3ac88
--- /dev/null
@@ -0,0 +1,11 @@
+Enable drivers in this directory like this:
+
+<?php
+function adminer_object() {
+       include "./plugins/drivers/simpledb.inc.php"; // the driver is enabled just by including
+       return new Adminer; // or return AdminerPlugin if you want to use other plugins
+}
+
+// include original Adminer
+include "./adminer.php";
+?>
diff --git a/plugins/drivers/clickhouse.inc.php b/plugins/drivers/clickhouse.inc.php
new file mode 100644 (file)
index 0000000..60bb0b4
--- /dev/null
@@ -0,0 +1,398 @@
+<?php
+add_driver("clickhouse", "ClickHouse (alpha)");
+
+if (isset($_GET["clickhouse"])) {
+       define("DRIVER", "clickhouse");
+
+       class Min_DB {
+               var $extension = "JSON", $server_info, $errno, $_result, $error, $_url;
+               var $_db = 'default';
+
+               function rootQuery($db, $query) {
+                       @ini_set('track_errors', 1); // @ - may be disabled
+                       $file = @file_get_contents("$this->_url/?database=$db", false, stream_context_create(array('http' => array(
+                               'method' => 'POST',
+                               'content' => $this->isQuerySelectLike($query) ? "$query FORMAT JSONCompact" : $query,
+                               'header' => 'Content-type: application/x-www-form-urlencoded',
+                               'ignore_errors' => 1, // available since PHP 5.2.10
+                       ))));
+
+                       if ($file === false) {
+                               $this->error = $php_errormsg;
+                               return $file;
+                       }
+                       if (!preg_match('~^HTTP/[0-9.]+ 2~i', $http_response_header[0])) {
+                               $this->error = lang('Invalid credentials.') . " $http_response_header[0]";
+                               return false;
+                       }
+                       $return = json_decode($file, true);
+                       if ($return === null) {
+                               if (!$this->isQuerySelectLike($query) && $file === '') {
+                                       return true;
+                               }
+
+                               $this->errno = json_last_error();
+                               if (function_exists('json_last_error_msg')) {
+                                       $this->error = json_last_error_msg();
+                               } else {
+                                       $constants = get_defined_constants(true);
+                                       foreach ($constants['json'] as $name => $value) {
+                                               if ($value == $this->errno && preg_match('~^JSON_ERROR_~', $name)) {
+                                                       $this->error = $name;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       return new Min_Result($return);
+               }
+
+               function isQuerySelectLike($query) {
+                       return (bool) preg_match('~^(select|show)~i', $query);
+               }
+
+               function query($query) {
+                       return $this->rootQuery($this->_db, $query);
+               }
+
+               function connect($server, $username, $password) {
+                       preg_match('~^(https?://)?(.*)~', $server, $match);
+                       $this->_url = ($match[1] ? $match[1] : "http://") . "$username:$password@$match[2]";
+                       $return = $this->query('SELECT 1');
+                       return (bool) $return;
+               }
+
+               function select_db($database) {
+                       $this->_db = $database;
+                       return true;
+               }
+
+               function quote($string) {
+                       return "'" . addcslashes($string, "\\'") . "'";
+               }
+
+               function multi_query($query) {
+                       return $this->_result = $this->query($query);
+               }
+
+               function store_result() {
+                       return $this->_result;
+               }
+
+               function next_result() {
+                       return false;
+               }
+
+               function result($query, $field = 0) {
+                       $result = $this->query($query);
+                       return $result['data'];
+               }
+       }
+
+       class Min_Result {
+               var $num_rows, $_rows, $columns, $meta, $_offset = 0;
+
+               function __construct($result) {
+                       $this->num_rows = $result['rows'];
+                       $this->_rows = $result['data'];
+                       $this->meta = $result['meta'];
+                       $this->columns = array_column($this->meta, 'name');
+                       reset($this->_rows);
+               }
+
+               function fetch_assoc() {
+                       $row = current($this->_rows);
+                       next($this->_rows);
+                       return $row === false ? false : array_combine($this->columns, $row);
+               }
+
+               function fetch_row() {
+                       $row = current($this->_rows);
+                       next($this->_rows);
+                       return $row;
+               }
+
+               function fetch_field() {
+                       $column = $this->_offset++;
+                       $return = new stdClass;
+                       if ($column < count($this->columns)) {
+                               $return->name = $this->meta[$column]['name'];
+                               $return->orgname = $return->name;
+                               $return->type = $this->meta[$column]['type'];
+                       }
+                       return $return;
+               }
+       }
+
+
+       class Min_Driver extends Min_SQL {
+               function delete($table, $queryWhere, $limit = 0) {
+                       if ($queryWhere === '') {
+                               $queryWhere = 'WHERE 1=1';
+                       }
+                       return queries("ALTER TABLE " . table($table) . " DELETE $queryWhere");
+               }
+
+               function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
+                       $values = array();
+                       foreach ($set as $key => $val) {
+                               $values[] = "$key = $val";
+                       }
+                       $query = $separator . implode(",$separator", $values);
+                       return queries("ALTER TABLE " . table($table) . " UPDATE $query$queryWhere");
+               }
+       }
+
+       function idf_escape($idf) {
+               return "`" . str_replace("`", "``", $idf) . "`";
+       }
+
+       function table($idf) {
+               return idf_escape($idf);
+       }
+
+       function explain($connection, $query) {
+               return '';
+       }
+
+       function found_rows($table_status, $where) {
+               $rows = get_vals("SELECT COUNT(*) FROM " . idf_escape($table_status["Name"]) . ($where ? " WHERE " . implode(" AND ", $where) : ""));
+               return empty($rows) ? false : $rows[0];
+       }
+
+       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
+               $alter = $order = array();
+               foreach ($fields as $field) {
+                       if ($field[1][2] === " NULL") {
+                               $field[1][1] = " Nullable({$field[1][1]})";
+                       } elseif ($field[1][2] === ' NOT NULL') {
+                               $field[1][2] = '';
+                       }
+
+                       if ($field[1][3]) {
+                               $field[1][3] = '';
+                       }
+
+                       $alter[] = ($field[1]
+                               ? ($table != "" ? ($field[0] != "" ? "MODIFY COLUMN " : "ADD COLUMN ") : " ") . implode($field[1])
+                               : "DROP COLUMN " . idf_escape($field[0])
+                       );
+
+                       $order[] = $field[1][0];
+               }
+
+               $alter = array_merge($alter, $foreign);
+               $status = ($engine ? " ENGINE " . $engine : "");
+               if ($table == "") {
+                       return queries("CREATE TABLE " . table($name) . " (\n" . implode(",\n", $alter) . "\n)$status$partitioning" . ' ORDER BY (' . implode(',', $order) . ')');
+               }
+               if ($table != $name) {
+                       $result = queries("RENAME TABLE " . table($table) . " TO " . table($name));
+                       if ($alter) {
+                               $table = $name;
+                       } else {
+                               return $result;
+                       }
+               }
+               if ($status) {
+                       $alter[] = ltrim($status);
+               }
+               return ($alter || $partitioning ? queries("ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter) . $partitioning) : true);
+       }
+
+       function truncate_tables($tables) {
+               return apply_queries("TRUNCATE TABLE", $tables);
+       }
+
+       function drop_views($views) {
+               return drop_tables($views);
+       }
+
+       function drop_tables($tables) {
+               return apply_queries("DROP TABLE", $tables);
+       }
+
+       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($flush) {
+               global $connection;
+               $result = get_rows('SHOW DATABASES');
+
+               $return = array();
+               foreach ($result as $row) {
+                       $return[] = $row['name'];
+               }
+               sort($return);
+               return $return;
+       }
+
+       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
+               return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" . ($offset ? ", $offset" : "") : "");
+       }
+
+       function limit1($table, $query, $where, $separator = "\n") {
+               return limit($query, $where, 1, 0, $separator);
+       }
+
+       function db_collation($db, $collations) {
+       }
+
+       function engines() {
+               return array('MergeTree');
+       }
+
+       function logged_user() {
+               global $adminer;
+               $credentials = $adminer->credentials();
+               return $credentials[1];
+       }
+
+       function tables_list() {
+               $result = get_rows('SHOW TABLES');
+               $return = array();
+               foreach ($result as $row) {
+                       $return[$row['name']] = 'table';
+               }
+               ksort($return);
+               return $return;
+       }
+
+       function count_tables($databases) {
+               return array();
+       }
+
+       function table_status($name = "", $fast = false) {
+               global $connection;
+               $return = array();
+               $tables = get_rows("SELECT name, engine FROM system.tables WHERE database = " . q($connection->_db));
+               foreach ($tables as $table) {
+                       $return[$table['name']] = array(
+                               'Name' => $table['name'],
+                               'Engine' => $table['engine'],
+                       );
+                       if ($name === $table['name']) {
+                               return $return[$table['name']];
+                       }
+               }
+               return $return;
+       }
+
+       function is_view($table_status) {
+               return false;
+       }
+
+       function fk_support($table_status) {
+               return false;
+       }
+
+       function convert_field($field) {
+       }
+
+       function unconvert_field($field, $return) {
+               if (in_array($field['type'], array("Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Float32", "Float64"))) {
+                       return "to$field[type]($return)";
+               }
+               return $return;
+       }
+
+       function fields($table) {
+               $return = array();
+               $result = get_rows("SELECT name, type, default_expression FROM system.columns WHERE " . idf_escape('table') . " = " . q($table));
+               foreach ($result as $row) {
+                       $type = trim($row['type']);
+                       $nullable = strpos($type, 'Nullable(') === 0;
+                       $return[trim($row['name'])] = array(
+                               "field" => trim($row['name']),
+                               "full_type" => $type,
+                               "type" => $type,
+                               "default" => trim($row['default_expression']),
+                               "null" => $nullable,
+                               "auto_increment" => '0',
+                               "privileges" => array("insert" => 1, "select" => 1, "update" => 0),
+                       );
+               }
+
+               return $return;
+       }
+
+       function indexes($table, $connection2 = null) {
+               return array();
+       }
+
+       function foreign_keys($table) {
+               return array();
+       }
+
+       function collations() {
+               return array();
+       }
+
+       function information_schema($db) {
+               return false;
+       }
+
+       function error() {
+               global $connection;
+               return h($connection->error);
+       }
+
+       function types() {
+               return array();
+       }
+
+       function schemas() {
+               return array();
+       }
+
+       function get_schema() {
+               return "";
+       }
+
+       function set_schema($schema) {
+               return true;
+       }
+
+       function auto_increment() {
+               return '';
+       }
+
+       function last_id() {
+               return 0; // ClickHouse doesn't have it
+       }
+
+       function support($feature) {
+               return preg_match("~^(columns|sql|status|table|drop_col)$~", $feature);
+       }
+
+       function driver_config() {
+               $types = array();
+               $structured_types = array();
+               foreach (array( //! arrays
+                       lang('Numbers') => array("Int8" => 3, "Int16" => 5, "Int32" => 10, "Int64" => 19, "UInt8" => 3, "UInt16" => 5, "UInt32" => 10, "UInt64" => 20, "Float32" => 7, "Float64" => 16, 'Decimal' => 38, 'Decimal32' => 9, 'Decimal64' => 18, 'Decimal128' => 38),
+                       lang('Date and time') => array("Date" => 13, "DateTime" => 20),
+                       lang('Strings') => array("String" => 0),
+                       lang('Binary') => array("FixedString" => 0),
+               ) as $key => $val) {
+                       $types += $val;
+                       $structured_types[$key] = array_keys($val);
+               }
+               return array(
+                       'jush' => "clickhouse",
+                       'types' => $types,
+                       'structured_types' => $structured_types,
+                       'unsigned' => array(),
+                       'operators' => array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"),
+                       'functions' => array(),
+                       'grouping' => array("avg", "count", "count distinct", "max", "min", "sum"),
+                       'edit_functions' => array(),
+               );
+       }
+}
diff --git a/plugins/drivers/firebird.inc.php b/plugins/drivers/firebird.inc.php
new file mode 100644 (file)
index 0000000..fc46f3a
--- /dev/null
@@ -0,0 +1,324 @@
+<?php
+/**
+* @author Steve Krämer
+*/
+
+add_driver('firebird', 'Firebird (alpha)');
+
+if (isset($_GET["firebird"])) {
+       define("DRIVER", "firebird");
+
+       if (extension_loaded("interbase") ) {
+               class Min_DB {
+                       var
+                               $extension = "Firebird",
+                               $server_info,
+                               $affected_rows,
+                               $errno,
+                               $error,
+                               $_link, $_result
+                       ;
+
+                       function connect($server, $username, $password) {
+                               $this->_link = ibase_connect($server, $username, $password); 
+                               if ($this->_link) {
+                                       $url_parts = explode(':', $server);
+                                       $this->service_link = ibase_service_attach($url_parts[0], $username, $password);
+                                       $this->server_info = ibase_server_info($this->service_link, IBASE_SVC_SERVER_VERSION);
+                               } else {
+                                       $this->errno = ibase_errcode();
+                                       $this->error = ibase_errmsg();
+                               }
+                               return (bool) $this->_link;
+                       }
+
+                       function quote($string) {
+                               return "'" . str_replace("'", "''", $string) . "'";
+                       }
+
+                       function select_db($database) {
+                               return ($database == "domain");
+                       }
+
+                       function query($query, $unbuffered = false) {
+                               $result = ibase_query($query, $this->_link);
+                               if (!$result) {
+                                       $this->errno = ibase_errcode();
+                                       $this->error = ibase_errmsg();
+                                       return false;
+                               }
+                               $this->error = "";
+                               if ($result === true) {
+                                       $this->affected_rows = ibase_affected_rows($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 false;
+                       }
+
+                       function result($query, $field = 0) {
+                               $result = $this->query($query);
+                               if (!$result || !$result->num_rows) {
+                                       return false;
+                               }
+                               $row = $result->fetch_row();
+                               return $row[$field];
+                       }
+               }
+
+               class Min_Result {
+                       var $num_rows, $_result, $_offset = 0;
+
+                       function __construct($result) {
+                               $this->_result = $result;
+                               // $this->num_rows = ibase_num_rows($result);
+                       }
+
+                       function fetch_assoc() {
+                               return ibase_fetch_assoc($this->_result);
+                       }
+
+                       function fetch_row() {
+                               return ibase_fetch_row($this->_result);
+                       }
+
+                       function fetch_field() {
+                               $field = ibase_field_info($this->_result, $this->_offset++);
+                               return (object) array(
+                                       'name' => $field['name'],
+                                       'orgname' => $field['name'],
+                                       'type' => $field['type'],
+                                       'charsetnr' => $field['length'],
+                               );
+                       }
+
+                       function __destruct() {
+                               ibase_free_result($this->_result);
+                       }
+               }
+
+       }
+       
+       
+       
+       class Min_Driver extends Min_SQL {
+       }
+
+
+
+       function idf_escape($idf) {
+               return '"' . str_replace('"', '""', $idf) . '"';
+       }
+
+       function table($idf) {
+               return idf_escape($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($flush) {
+               return array("domain");
+       }
+
+       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
+               $return = ''; 
+               $return .= ($limit !== null ? $separator . "FIRST $limit" . ($offset ? " SKIP $offset" : "") : "");
+               $return .= " $query$where";
+               return $return;
+       }
+
+       function limit1($table, $query, $where, $separator = "\n") {
+               return limit($query, $where, 1, 0, $separator);
+       }
+
+       function db_collation($db, $collations) {
+       }
+
+       function engines() {
+               return array();
+       }
+
+       function logged_user() {
+               global $adminer;
+               $credentials = $adminer->credentials();
+               return $credentials[1];
+       }
+
+       function tables_list() {
+               global $connection;
+               $query = 'SELECT RDB$RELATION_NAME FROM rdb$relations WHERE rdb$system_flag = 0';
+               $result = ibase_query($connection->_link, $query);
+               $return = array();
+               while ($row = ibase_fetch_assoc($result)) {
+                               $return[$row['RDB$RELATION_NAME']] = 'table';
+               }
+               ksort($return); 
+               return $return;
+       }
+
+       function count_tables($databases) {
+               return array();
+       }
+
+       function table_status($name = "", $fast = false) {
+               global $connection;
+               $return = array();
+               $data = tables_list();
+               foreach ($data as $index => $val) {
+                       $index = trim($index);
+                       $return[$index] = array(
+                               'Name' => $index,
+                               'Engine' => 'standard',
+                       );
+                       if ($name == $index) {
+                               return $return[$index];
+                       }
+               }
+               return $return;
+       }
+
+       function is_view($table_status) {
+               return false;
+       }
+
+       function fk_support($table_status) {
+               return preg_match('~InnoDB|IBMDB2I~i', $table_status["Engine"]);
+       }
+
+       function fields($table) {
+               global $connection;
+               $return = array();
+               $query = 'SELECT r.RDB$FIELD_NAME AS field_name,
+r.RDB$DESCRIPTION AS field_description,
+r.RDB$DEFAULT_VALUE AS field_default_value,
+r.RDB$NULL_FLAG AS field_not_null_constraint,
+f.RDB$FIELD_LENGTH AS field_length,
+f.RDB$FIELD_PRECISION AS field_precision,
+f.RDB$FIELD_SCALE AS field_scale,
+CASE f.RDB$FIELD_TYPE
+WHEN 261 THEN \'BLOB\'
+WHEN 14 THEN \'CHAR\'
+WHEN 40 THEN \'CSTRING\'
+WHEN 11 THEN \'D_FLOAT\'
+WHEN 27 THEN \'DOUBLE\'
+WHEN 10 THEN \'FLOAT\'
+WHEN 16 THEN \'INT64\'
+WHEN 8 THEN \'INTEGER\'
+WHEN 9 THEN \'QUAD\'
+WHEN 7 THEN \'SMALLINT\'
+WHEN 12 THEN \'DATE\'
+WHEN 13 THEN \'TIME\'
+WHEN 35 THEN \'TIMESTAMP\'
+WHEN 37 THEN \'VARCHAR\'
+ELSE \'UNKNOWN\'
+END AS field_type,
+f.RDB$FIELD_SUB_TYPE AS field_subtype,
+coll.RDB$COLLATION_NAME AS field_collation,
+cset.RDB$CHARACTER_SET_NAME AS field_charset
+FROM RDB$RELATION_FIELDS r
+LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME
+LEFT JOIN RDB$COLLATIONS coll ON f.RDB$COLLATION_ID = coll.RDB$COLLATION_ID
+LEFT JOIN RDB$CHARACTER_SETS cset ON f.RDB$CHARACTER_SET_ID = cset.RDB$CHARACTER_SET_ID
+WHERE r.RDB$RELATION_NAME = ' . q($table) . '
+ORDER BY r.RDB$FIELD_POSITION';
+               $result = ibase_query($connection->_link, $query);
+               while ($row = ibase_fetch_assoc($result)) {
+                       $return[trim($row['FIELD_NAME'])] = array(
+                               "field" => trim($row["FIELD_NAME"]),
+                               "full_type" => trim($row["FIELD_TYPE"]),
+                               "type" => trim($row["FIELD_SUB_TYPE"]),
+                               "default" => trim($row['FIELD_DEFAULT_VALUE']),
+                               "null" => (trim($row["FIELD_NOT_NULL_CONSTRAINT"]) == "YES"),
+                               "auto_increment" => '0',
+                               "collation" => trim($row["FIELD_COLLATION"]),
+                               "privileges" => array("insert" => 1, "select" => 1, "update" => 1),
+                               "comment" => trim($row["FIELD_DESCRIPTION"]),
+                       );
+               }
+               return $return;
+       }
+
+       function indexes($table, $connection2 = null) {
+               $return = array();
+               /*
+               $query = 'SELECT RDB$INDEX_SEGMENTS.RDB$FIELD_NAME AS field_name,
+RDB$INDICES.RDB$DESCRIPTION AS description,
+(RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION + 1) AS field_position
+FROM RDB$INDEX_SEGMENTS
+LEFT JOIN RDB$INDICES ON RDB$INDICES.RDB$INDEX_NAME = RDB$INDEX_SEGMENTS.RDB$INDEX_NAME
+LEFT JOIN RDB$RELATION_CONSTRAINTS ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDEX_SEGMENTS.RDB$INDEX_NAME
+WHERE UPPER(RDB$INDICES.RDB$RELATION_NAME) = ' . q($table) . '
+// AND UPPER(RDB$INDICES.RDB$INDEX_NAME) = \'TEST2_FIELD5_IDX\'
+AND RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE IS NULL
+ORDER BY RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION';
+               */
+               return $return;
+       }
+
+       function foreign_keys($table) {
+               return array();
+       }
+
+       function collations() {
+               return array();
+       }
+
+       function information_schema($db) {
+               return false;
+       }
+
+       function error() {
+               global $connection;
+               return h($connection->error);
+       }
+
+       function types() {
+               return array();
+       }
+
+       function schemas() {
+               return array();
+       }
+
+       function get_schema() {
+               return "";
+       }
+
+       function set_schema($schema) {
+               return true;
+       }
+
+       function support($feature) {
+               return preg_match("~^(columns|sql|status|table)$~", $feature);
+       }
+
+       function driver_config() {
+               return array(
+                       'possible_drivers' => array("interbase"),
+                       'jush' => "firebird",
+                       'operators' => array("="),
+                       'functions' => array(),
+                       'grouping' => array(),
+                       'edit_functions' => array(),
+               );
+       }
+}
diff --git a/plugins/drivers/simpledb.inc.php b/plugins/drivers/simpledb.inc.php
new file mode 100644 (file)
index 0000000..9fafe88
--- /dev/null
@@ -0,0 +1,488 @@
+<?php
+add_driver("simpledb", "SimpleDB");
+
+if (isset($_GET["simpledb"])) {
+       define("DRIVER", "simpledb");
+
+       if (class_exists('SimpleXMLElement') && ini_bool('allow_url_fopen')) {
+               class Min_DB {
+                       var $extension = "SimpleXML", $server_info = '2009-04-15', $error, $timeout, $next, $affected_rows, $_result;
+
+                       function select_db($database) {
+                               return ($database == "domain");
+                       }
+
+                       function query($query, $unbuffered = false) {
+                               $params = array('SelectExpression' => $query, 'ConsistentRead' => 'true');
+                               if ($this->next) {
+                                       $params['NextToken'] = $this->next;
+                               }
+                               $result = sdb_request_all('Select', 'Item', $params, $this->timeout); //! respect $unbuffered
+                               $this->timeout = 0;
+                               if ($result === false) {
+                                       return $result;
+                               }
+                               if (preg_match('~^\s*SELECT\s+COUNT\(~i', $query)) {
+                                       $sum = 0;
+                                       foreach ($result as $item) {
+                                               $sum += $item->Attribute->Value;
+                                       }
+                                       $result = array((object) array('Attribute' => array((object) array(
+                                               'Name' => 'Count',
+                                               'Value' => $sum,
+                                       ))));
+                               }
+                               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 false;
+                       }
+
+                       function quote($string) {
+                               return "'" . str_replace("'", "''", $string) . "'";
+                       }
+
+               }
+
+               class Min_Result {
+                       var $num_rows, $_rows = array(), $_offset = 0;
+
+                       function __construct($result) {
+                               foreach ($result as $item) {
+                                       $row = array();
+                                       if ($item->Name != '') { // SELECT COUNT(*)
+                                               $row['itemName()'] = (string) $item->Name;
+                                       }
+                                       foreach ($item->Attribute as $attribute) {
+                                               $name = $this->_processValue($attribute->Name);
+                                               $value = $this->_processValue($attribute->Value);
+                                               if (isset($row[$name])) {
+                                                       $row[$name] = (array) $row[$name];
+                                                       $row[$name][] = $value;
+                                               } else {
+                                                       $row[$name] = $value;
+                                               }
+                                       }
+                                       $this->_rows[] = $row;
+                                       foreach ($row as $key => $val) {
+                                               if (!isset($this->_rows[0][$key])) {
+                                                       $this->_rows[0][$key] = null;
+                                               }
+                                       }
+                               }
+                               $this->num_rows = count($this->_rows);
+                       }
+
+                       function _processValue($element) {
+                               return (is_object($element) && $element['encoding'] == 'base64' ? base64_decode($element) : (string) $element);
+                       }
+
+                       function fetch_assoc() {
+                               $row = current($this->_rows);
+                               if (!$row) {
+                                       return $row;
+                               }
+                               $return = array();
+                               foreach ($this->_rows[0] as $key => $val) {
+                                       $return[$key] = $row[$key];
+                               }
+                               next($this->_rows);
+                               return $return;
+                       }
+
+                       function fetch_row() {
+                               $return = $this->fetch_assoc();
+                               if (!$return) {
+                                       return $return;
+                               }
+                               return array_values($return);
+                       }
+
+                       function fetch_field() {
+                               $keys = array_keys($this->_rows[0]);
+                               return (object) array('name' => $keys[$this->_offset++]);
+                       }
+
+               }
+       }
+
+
+
+       class Min_Driver extends Min_SQL {
+               public $primary = "itemName()";
+
+               function _chunkRequest($ids, $action, $params, $expand = array()) {
+                       global $connection;
+                       foreach (array_chunk($ids, 25) as $chunk) {
+                               $params2 = $params;
+                               foreach ($chunk as $i => $id) {
+                                       $params2["Item.$i.ItemName"] = $id;
+                                       foreach ($expand as $key => $val) {
+                                               $params2["Item.$i.$key"] = $val;
+                                       }
+                               }
+                               if (!sdb_request($action, $params2)) {
+                                       return false;
+                               }
+                       }
+                       $connection->affected_rows = count($ids);
+                       return true;
+               }
+
+               function _extractIds($table, $queryWhere, $limit) {
+                       $return = array();
+                       if (preg_match_all("~itemName\(\) = (('[^']*+')+)~", $queryWhere, $matches)) {
+                               $return = array_map('idf_unescape', $matches[1]);
+                       } else {
+                               foreach (sdb_request_all('Select', 'Item', array('SelectExpression' => 'SELECT itemName() FROM ' . table($table) . $queryWhere . ($limit ? " LIMIT 1" : ""))) as $item) {
+                                       $return[] = $item->Name;
+                               }
+                       }
+                       return $return;
+               }
+
+               function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
+                       global $connection;
+                       $connection->next = $_GET["next"];
+                       $return = parent::select($table, $select, $where, $group, $order, $limit, $page, $print);
+                       $connection->next = 0;
+                       return $return;
+               }
+
+               function delete($table, $queryWhere, $limit = 0) {
+                       return $this->_chunkRequest(
+                               $this->_extractIds($table, $queryWhere, $limit),
+                               'BatchDeleteAttributes',
+                               array('DomainName' => $table)
+                       );
+               }
+
+               function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
+                       $delete = array();
+                       $insert = array();
+                       $i = 0;
+                       $ids = $this->_extractIds($table, $queryWhere, $limit);
+                       $id = idf_unescape($set["`itemName()`"]);
+                       unset($set["`itemName()`"]);
+                       foreach ($set as $key => $val) {
+                               $key = idf_unescape($key);
+                               if ($val == "NULL" || ($id != "" && array($id) != $ids)) {
+                                       $delete["Attribute." . count($delete) . ".Name"] = $key;
+                               }
+                               if ($val != "NULL") {
+                                       foreach ((array) $val as $k => $v) {
+                                               $insert["Attribute.$i.Name"] = $key;
+                                               $insert["Attribute.$i.Value"] = (is_array($val) ? $v : idf_unescape($v));
+                                               if (!$k) {
+                                                       $insert["Attribute.$i.Replace"] = "true";
+                                               }
+                                               $i++;
+                                       }
+                               }
+                       }
+                       $params = array('DomainName' => $table);
+                       return (!$insert || $this->_chunkRequest(($id != "" ? array($id) : $ids), 'BatchPutAttributes', $params, $insert))
+                               && (!$delete || $this->_chunkRequest($ids, 'BatchDeleteAttributes', $params, $delete))
+                       ;
+               }
+
+               function insert($table, $set) {
+                       $params = array("DomainName" => $table);
+                       $i = 0;
+                       foreach ($set as $name => $value) {
+                               if ($value != "NULL") {
+                                       $name = idf_unescape($name);
+                                       if ($name == "itemName()") {
+                                               $params["ItemName"] = idf_unescape($value);
+                                       } else {
+                                               foreach ((array) $value as $val) {
+                                                       $params["Attribute.$i.Name"] = $name;
+                                                       $params["Attribute.$i.Value"] = (is_array($value) ? $val : idf_unescape($value));
+                                                       $i++;
+                                               }
+                                       }
+                               }
+                       }
+                       return sdb_request('PutAttributes', $params);
+               }
+
+               function insertUpdate($table, $rows, $primary) {
+                       //! use one batch request
+                       foreach ($rows as $set) {
+                               if (!$this->update($table, $set, "WHERE `itemName()` = " . q($set["`itemName()`"]))) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               }
+
+               function begin() {
+                       return false;
+               }
+
+               function commit() {
+                       return false;
+               }
+
+               function rollback() {
+                       return false;
+               }
+               
+               function slowQuery($query, $timeout) {
+                       $this->_conn->timeout = $timeout;
+                       return $query;
+               }
+
+       }
+
+
+
+       function connect() {
+               global $adminer;
+               list(, , $password) = $adminer->credentials();
+               if ($password != "") {
+                       return lang('Database does not support password.');
+               }
+               return new Min_DB;
+       }
+
+       function support($feature) {
+               return preg_match('~sql~', $feature);
+       }
+
+       function logged_user() {
+               global $adminer;
+               $credentials = $adminer->credentials();
+               return $credentials[1];
+       }
+
+       function get_databases() {
+               return array("domain");
+       }
+
+       function collations() {
+               return array();
+       }
+
+       function db_collation($db, $collations) {
+       }
+
+       function tables_list() {
+               global $connection;
+               $return = array();
+               foreach (sdb_request_all('ListDomains', 'DomainName') as $table) {
+                       $return[(string) $table] = 'table';
+               }
+               if ($connection->error && defined("PAGE_HEADER")) {
+                       echo "<p class='error'>" . error() . "\n";
+               }
+               return $return;
+       }
+
+       function table_status($name = "", $fast = false) {
+               $return = array();
+               foreach (($name != "" ? array($name => true) : tables_list()) as $table => $type) {
+                       $row = array("Name" => $table, "Auto_increment" => "");
+                       if (!$fast) {
+                               $meta = sdb_request('DomainMetadata', array('DomainName' => $table));
+                               if ($meta) {
+                                       foreach (array(
+                                               "Rows" => "ItemCount",
+                                               "Data_length" => "ItemNamesSizeBytes",
+                                               "Index_length" => "AttributeValuesSizeBytes",
+                                               "Data_free" => "AttributeNamesSizeBytes",
+                                       ) as $key => $val) {
+                                               $row[$key] = (string) $meta->$val;
+                                       }
+                               }
+                       }
+                       if ($name != "") {
+                               return $row;
+                       }
+                       $return[$table] = $row;
+               }
+               return $return;
+       }
+
+       function explain($connection, $query) {
+       }
+
+       function error() {
+               global $connection;
+               return h($connection->error);
+       }
+
+       function information_schema() {
+       }
+
+       function is_view($table_status) {
+       }
+
+       function indexes($table, $connection2 = null) {
+               return array(
+                       array("type" => "PRIMARY", "columns" => array("itemName()")),
+               );
+       }
+
+       function fields($table) {
+               return fields_from_edit();
+       }
+
+       function foreign_keys($table) {
+               return array();
+       }
+
+       function table($idf) {
+               return idf_escape($idf);
+       }
+
+       function idf_escape($idf) {
+               return "`" . str_replace("`", "``", $idf) . "`";
+       }
+
+       function limit($query, $where, $limit, $offset = 0, $separator = " ") {
+               return " $query$where" . ($limit !== null ? $separator . "LIMIT $limit" : "");
+       }
+
+       function unconvert_field($field, $return) {
+               return $return;
+       }
+
+       function fk_support($table_status) {
+       }
+
+       function engines() {
+               return array();
+       }
+
+       function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
+               return ($table == "" && sdb_request('CreateDomain', array('DomainName' => $name)));
+       }
+
+       function drop_tables($tables) {
+               foreach ($tables as $table) {
+                       if (!sdb_request('DeleteDomain', array('DomainName' => $table))) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       function count_tables($databases) {
+               foreach ($databases as $db) {
+                       return array($db => count(tables_list()));
+               }
+       }
+
+       function found_rows($table_status, $where) {
+               return ($where ? null : $table_status["Rows"]);
+       }
+
+       function last_id() {
+       }
+
+       function hmac($algo, $data, $key, $raw_output = false) {
+               // can use hash_hmac() since PHP 5.1.2
+               $blocksize = 64;
+               if (strlen($key) > $blocksize) {
+                       $key = pack("H*", $algo($key));
+               }
+               $key = str_pad($key, $blocksize, "\0");
+               $k_ipad = $key ^ str_repeat("\x36", $blocksize);
+               $k_opad = $key ^ str_repeat("\x5C", $blocksize);
+               $return = $algo($k_opad . pack("H*", $algo($k_ipad . $data)));
+               if ($raw_output) {
+                       $return = pack("H*", $return);
+               }
+               return $return;
+       }
+
+       function sdb_request($action, $params = array()) {
+               global $adminer, $connection;
+               list($host, $params['AWSAccessKeyId'], $secret) = $adminer->credentials();
+               $params['Action'] = $action;
+               $params['Timestamp'] = gmdate('Y-m-d\TH:i:s+00:00');
+               $params['Version'] = '2009-04-15';
+               $params['SignatureVersion'] = 2;
+               $params['SignatureMethod'] = 'HmacSHA1';
+               ksort($params);
+               $query = '';
+               foreach ($params as $key => $val) {
+                       $query .= '&' . rawurlencode($key) . '=' . rawurlencode($val);
+               }
+               $query = str_replace('%7E', '~', substr($query, 1));
+               $query .= "&Signature=" . urlencode(base64_encode(hmac('sha1', "POST\n" . preg_replace('~^https?://~', '', $host) . "\n/\n$query", $secret, true)));
+               @ini_set('track_errors', 1); // @ - may be disabled
+               $file = @file_get_contents((preg_match('~^https?://~', $host) ? $host : "http://$host"), false, stream_context_create(array('http' => array(
+                       'method' => 'POST', // may not fit in URL with GET
+                       'content' => $query,
+                       'ignore_errors' => 1, // available since PHP 5.2.10
+               ))));
+               if (!$file) {
+                       $connection->error = $php_errormsg;
+                       return false;
+               }
+               libxml_use_internal_errors(true);
+               $xml = simplexml_load_string($file);
+               if (!$xml) {
+                       $error = libxml_get_last_error();
+                       $connection->error = $error->message;
+                       return false;
+               }
+               if ($xml->Errors) {
+                       $error = $xml->Errors->Error;
+                       $connection->error = "$error->Message ($error->Code)";
+                       return false;
+               }
+               $connection->error = '';
+               $tag = $action . "Result";
+               return ($xml->$tag ? $xml->$tag : true);
+       }
+
+       function sdb_request_all($action, $tag, $params = array(), $timeout = 0) {
+               $return = array();
+               $start = ($timeout ? microtime(true) : 0);
+               $limit = (preg_match('~LIMIT\s+(\d+)\s*$~i', $params['SelectExpression'], $match) ? $match[1] : 0);
+               do {
+                       $xml = sdb_request($action, $params);
+                       if (!$xml) {
+                               break;
+                       }
+                       foreach ($xml->$tag as $element) {
+                               $return[] = $element;
+                       }
+                       if ($limit && count($return) >= $limit) {
+                               $_GET["next"] = $xml->NextToken;
+                               break;
+                       }
+                       if ($timeout && microtime(true) - $start > $timeout) {
+                               return false;
+                       }
+                       $params['NextToken'] = $xml->NextToken;
+                       if ($limit) {
+                               $params['SelectExpression'] = preg_replace('~\d+\s*$~', $limit - count($return), $params['SelectExpression']);
+                       }
+               } while ($xml->NextToken);
+               return $return;
+       }
+
+       function driver_config() {
+               return array(
+                       'possible_drivers' => array("SimpleXML + allow_url_fopen"),
+                       'jush' => "simpledb",
+                       'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "IS NOT NULL"),
+                       'functions' => array(),
+                       'grouping' => array("count"),
+                       'edit_functions' => array(array("json")),
+               );
+       }
+}