diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000..b681ea31
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,67 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ master ]
+ schedule:
+ - cron: '20 16 * * 0'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'javascript' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+ # Learn more:
+ # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.gitmodules b/.gitmodules
index 63ed7709..5810a5e7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,9 @@
[submodule "JsShrink"]
path = externals/JsShrink
url = git://github.com/vrana/JsShrink
+[submodule "designs/hydra"]
+ path = designs/hydra
+ url = https://github.com/Niyko/Hydra-Dark-Theme-for-Adminer
+[submodule "designs/pepa-linha-dark"]
+ path = designs/pepa-linha-dark
+ url = https://github.com/pepa-linha/Adminer-Design-Dark/
diff --git a/.travis.yml b/.travis.yml
index 213081d3..937ea7c7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,4 +4,6 @@ php:
- 7.1
- 7.2
- 7.3
+ - 7.4
+ - 8.0
script: git diff --name-only $TRAVIS_COMMIT_RANGE | grep '\.php$' | xargs -n1 -P8 php -l | grep -v 'No syntax errors'; test $? -eq 1
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..8c5dcfbf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+ROOT_DIRECTORY = $(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))")
+PHP := $(shell which php)
+PORT := 8000
+
+
+.DEFAULT_GOAL := default
+
+
+.PHONY: default
+default: compile
+
+.PHONY: compile
+compile:
+ $(PHP) $(ROOT_DIRECTORY)/compile.php
+
+.PHONY: server
+server:
+ php \
+ --server 127.0.0.1:$(PORT) \
+ --docroot $(ROOT_DIRECTORY)
+
+.PHONY: initialize
+initialize:
+ git \
+ -C $(ROOT_DIRECTORY) \
+ submodule \
+ update \
+ --init \
+ --recursive
+
+.PHONY: clean
+clean:
+ rm \
+ --recursive \
+ --force \
+ $(ROOT_DIRECTORY)/adminer.php
+
+.PHONY: clean.all
+clean.all: clean
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..c68b159e
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+
+I support only the last published version and the last development version (last commit).
+
+## Reporting a Vulnerability
+
+To report a vulnerability, create a private bug at https://sourceforge.net/p/adminer/bugs-and-features/new/?private=1.
+
+I handle security issues with top priority. If you don't hear from me in a week then please ping the bug. Once I accept the bug, the fix should be available and new version released within days. I will mark the bug as public after releasing a new version or declining the bug.
diff --git a/adminer/call.inc.php b/adminer/call.inc.php
index 8a7c8a00..f8988675 100644
--- a/adminer/call.inc.php
+++ b/adminer/call.inc.php
@@ -48,7 +48,9 @@ if (!$error && $_POST) {
if (is_object($result)) {
select($result, $connection2);
} else {
- echo "
" . lang('Routine has been called, %d row(s) affected.', $affected) . "\n";
+ echo "
" . lang('Routine has been called, %d row(s) affected.', $affected)
+ . " " . @date("H:i:s") . " \n" // @ - time zone may be not set
+ ;
}
} while ($connection->next_result());
diff --git a/adminer/create.inc.php b/adminer/create.inc.php
index 47e7d065..03acfe42 100644
--- a/adminer/create.inc.php
+++ b/adminer/create.inc.php
@@ -54,7 +54,7 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
}
$process_field = process_field($field, $type_field);
$all_fields[] = array($field["orig"], $process_field, $after);
- if ($process_field != process_field($orig_field, $orig_field)) {
+ if (!$orig_field || $process_field != process_field($orig_field, $orig_field)) {
$fields[] = array($field["orig"], $process_field, $after);
if ($field["orig"] != "" || $after) {
$use_all_fields = true;
@@ -185,11 +185,17 @@ edit_fields($row["fields"], $collations, "TABLE", $foreign_keys);
" . lang('%d in total', count($tables_list));
- echo " " . h($jush == "sql" ? $connection->result("SELECT @@storage_engine") : "");
+ echo " " . h($jush == "sql" ? $connection->result("SELECT @@default_storage_engine") : "");
echo " " . h(db_collation(DB, collations()));
foreach (array("Data_length", "Index_length", "Data_free") as $key) {
echo " ";
diff --git a/adminer/drivers/elastic.inc.php b/adminer/drivers/elastic.inc.php
index a29252fe..799f6d0b 100644
--- a/adminer/drivers/elastic.inc.php
+++ b/adminer/drivers/elastic.inc.php
@@ -2,12 +2,11 @@
$drivers["elastic"] = "Elasticsearch (beta)";
if (isset($_GET["elastic"])) {
- $possible_drivers = array("json + allow_url_fopen");
define("DRIVER", "elastic");
if (function_exists('json_decode') && ini_bool('allow_url_fopen')) {
class Min_DB {
- var $extension = "JSON", $server_info, $errno, $error, $_url;
+ var $extension = "JSON", $server_info, $errno, $error, $_url, $_db;
/** Performs query
* @param string
@@ -28,7 +27,7 @@ if (isset($_GET["elastic"])) {
return $file;
}
if (!preg_match('~^HTTP/[0-9.]+ 2~i', $http_response_header[0])) {
- $this->error = $file;
+ $this->error = lang('Invalid credentials.') . " $http_response_header[0]";
return false;
}
$return = json_decode($file, true);
@@ -284,6 +283,11 @@ if (isset($_GET["elastic"])) {
function tables_list() {
global $connection;
+
+ if (min_version(6)) {
+ return array('_doc' => 'table');
+ }
+
$return = $connection->query('_mapping');
if ($return) {
$return = array_fill_keys(array_keys($return[$connection->_db]["mappings"]), 'table');
@@ -339,25 +343,35 @@ if (isset($_GET["elastic"])) {
function fields($table) {
global $connection;
- $result = $connection->query("$table/_mapping");
- $return = array();
- if ($result) {
- $mappings = $result[$table]['properties'];
- if (!$mappings) {
- $mappings = $result[$connection->_db]['mappings'][$table]['properties'];
+
+ $mappings = array();
+ if (min_version(6)) {
+ $result = $connection->query("_mapping");
+ if ($result) {
+ $mappings = $result[$connection->_db]['mappings']['properties'];
}
- if ($mappings) {
- foreach ($mappings as $name => $field) {
- $return[$name] = array(
- "field" => $name,
- "full_type" => $field["type"],
- "type" => $field["type"],
- "privileges" => array("insert" => 1, "select" => 1, "update" => 1),
- );
- if ($field["properties"]) { // only leaf fields can be edited
- unset($return[$name]["privileges"]["insert"]);
- unset($return[$name]["privileges"]["update"]);
- }
+ } else {
+ $result = $connection->query("$table/_mapping");
+ if ($result) {
+ $mappings = $result[$table]['properties'];
+ if (!$mappings) {
+ $mappings = $result[$connection->_db]['mappings'][$table]['properties'];
+ }
+ }
+ }
+
+ $return = array();
+ if ($mappings) {
+ foreach ($mappings as $name => $field) {
+ $return[$name] = array(
+ "field" => $name,
+ "full_type" => $field["type"],
+ "type" => $field["type"],
+ "privileges" => array("insert" => 1, "select" => 1, "update" => 1),
+ );
+ if ($field["properties"]) { // only leaf fields can be edited
+ unset($return[$name]["privileges"]["insert"]);
+ unset($return[$name]["privileges"]["update"]);
}
}
}
@@ -446,20 +460,27 @@ if (isset($_GET["elastic"])) {
return $connection->last_id;
}
- $jush = "elastic";
- $operators = array("=", "query");
- $functions = array();
- $grouping = array();
- $edit_functions = array(array("json"));
- $types = array(); ///< @var array ($type => $maximum_unsigned_length, ...)
- $structured_types = array(); ///< @var array ($description => array($type, ...), ...)
- foreach (array(
- lang('Numbers') => array("long" => 3, "integer" => 5, "short" => 8, "byte" => 10, "double" => 20, "float" => 66, "half_float" => 12, "scaled_float" => 21),
- lang('Date and time') => array("date" => 10),
- lang('Strings') => array("string" => 65535, "text" => 65535),
- lang('Binary') => array("binary" => 255),
- ) as $key => $val) {
- $types += $val;
- $structured_types[$key] = array_keys($val);
+ function driver_config() {
+ $types = array();
+ $structured_types = array();
+ foreach (array(
+ lang('Numbers') => array("long" => 3, "integer" => 5, "short" => 8, "byte" => 10, "double" => 20, "float" => 66, "half_float" => 12, "scaled_float" => 21),
+ lang('Date and time') => array("date" => 10),
+ lang('Strings') => array("string" => 65535, "text" => 65535),
+ lang('Binary') => array("binary" => 255),
+ ) as $key => $val) {
+ $types += $val;
+ $structured_types[$key] = array_keys($val);
+ }
+ return array(
+ 'possible_drivers' => array("json + allow_url_fopen"),
+ 'jush' => "elastic",
+ 'operators' => array("=", "query"),
+ 'functions' => array(),
+ 'grouping' => array(),
+ 'edit_functions' => array(array("json")),
+ 'types' => $types,
+ 'structured_types' => $structured_types,
+ );
}
}
diff --git a/adminer/drivers/mongo.inc.php b/adminer/drivers/mongo.inc.php
index 88c8e0db..63649f04 100644
--- a/adminer/drivers/mongo.inc.php
+++ b/adminer/drivers/mongo.inc.php
@@ -1,8 +1,7 @@
_link = new MongoClient($uri, $options);
+ if ($options["password"] != "") {
+ $options["password"] = "";
+ try {
+ new MongoClient($uri, $options);
+ $this->error = lang('Database does not support password.');
+ } catch (Exception $e) {
+ // this is what we want
+ }
+ }
+ } catch (Exception $e) {
+ $this->error = $e->getMessage();
+ }
}
function query($query) {
@@ -30,7 +42,6 @@ if (isset($_GET["mongo"])) {
function quote($string) {
return $string;
}
-
}
class Min_Result {
@@ -44,10 +55,10 @@ if (isset($_GET["mongo"])) {
$this->_charset[$key] = 63;
}
$row[$key] =
- (is_a($val, 'MongoId') ? 'ObjectId("' . strval($val) . '")' :
+ (is_a($val, 'MongoId') ? "ObjectId(\"$val\")" :
(is_a($val, 'MongoDate') ? gmdate("Y-m-d H:i:s", $val->sec) . " GMT" :
(is_a($val, 'MongoBinData') ? $val->bin : //! allow downloading
- (is_a($val, 'MongoRegex') ? strval($val) :
+ (is_a($val, 'MongoRegex') ? "$val" :
(is_object($val) ? get_class($val) : // MongoMinKey, MongoMaxKey
$val
)))));
@@ -198,14 +209,36 @@ if (isset($_GET["mongo"])) {
} elseif (class_exists('MongoDB\Driver\Manager')) {
class Min_DB {
- var $extension = "MongoDB", $server_info = MONGODB_VERSION, $error, $last_id;
+ var $extension = "MongoDB", $server_info = MONGODB_VERSION, $affected_rows, $error, $last_id;
/** @var MongoDB\Driver\Manager */
var $_link;
var $_db, $_db_name;
function connect($uri, $options) {
$class = 'MongoDB\Driver\Manager';
- return new $class($uri, $options);
+ $this->_link = new $class($uri, $options);
+ $this->executeCommand('admin', array('ping' => 1));
+ }
+
+ function executeCommand($db, $command) {
+ $class = 'MongoDB\Driver\Command';
+ try {
+ return $this->_link->executeCommand($db, new $class($command));
+ } catch (Exception $e) {
+ $this->error = $e->getMessage();
+ return array();
+ }
+ }
+
+ function executeBulkWrite($namespace, $bulk, $counter) {
+ try {
+ $results = $this->_link->executeBulkWrite($namespace, $bulk);
+ $this->affected_rows = $results->$counter();
+ return true;
+ } catch (Exception $e) {
+ $this->error = $e->getMessage();
+ return false;
+ }
}
function query($query) {
@@ -220,7 +253,6 @@ if (isset($_GET["mongo"])) {
function quote($string) {
return $string;
}
-
}
class Min_Result {
@@ -234,13 +266,13 @@ if (isset($_GET["mongo"])) {
$this->_charset[$key] = 63;
}
$row[$key] =
- (is_a($val, 'MongoDB\BSON\ObjectID') ? 'MongoDB\BSON\ObjectID("' . strval($val) . '")' :
- (is_a($val, 'MongoDB\BSON\UTCDatetime') ? $val->toDateTime()->format('Y-m-d H:i:s') :
- (is_a($val, 'MongoDB\BSON\Binary') ? $val->bin : //! allow downloading
- (is_a($val, 'MongoDB\BSON\Regex') ? strval($val) :
- (is_object($val) ? json_encode($val, 256) : // 256 = JSON_UNESCAPED_UNICODE
- $val // MongoMinKey, MongoMaxKey
- )))));
+ (is_a($val, 'MongoDB\BSON\ObjectID') ? 'MongoDB\BSON\ObjectID("' . "$val\")" :
+ (is_a($val, 'MongoDB\BSON\UTCDatetime') ? $val->toDateTime()->format('Y-m-d H:i:s') :
+ (is_a($val, 'MongoDB\BSON\Binary') ? $val->getData() : //! allow downloading
+ (is_a($val, 'MongoDB\BSON\Regex') ? "$val" :
+ (is_object($val) || is_array($val) ? json_encode($val, 256) : // 256 = JSON_UNESCAPED_UNICODE
+ $val // MongoMinKey, MongoMaxKey
+ )))));
}
$this->_rows[] = $row;
foreach ($row as $key => $val) {
@@ -249,7 +281,7 @@ if (isset($_GET["mongo"])) {
}
}
}
- $this->num_rows = $result->count;
+ $this->num_rows = count($this->_rows);
}
function fetch_assoc() {
@@ -309,9 +341,12 @@ if (isset($_GET["mongo"])) {
$limit = min(200, max(1, (int) $limit));
$skip = $page * $limit;
$class = 'MongoDB\Driver\Query';
- $query = new $class($where, array('projection' => $select, 'limit' => $limit, 'skip' => $skip, 'sort' => $sort));
- $results = $connection->_link->executeQuery("$connection->_db_name.$table", $query);
- return new Min_Result($results);
+ try {
+ return new Min_Result($connection->_link->executeQuery("$connection->_db_name.$table", new $class($where, array('projection' => $select, 'limit' => $limit, 'skip' => $skip, 'sort' => $sort))));
+ } catch (Exception $e) {
+ $connection->error = $e->getMessage();
+ return false;
+ }
}
function update($table, $set, $queryWhere, $limit = 0, $separator = "\n") {
@@ -335,9 +370,7 @@ if (isset($_GET["mongo"])) {
$update['$unset'] = $removeFields;
}
$bulk->update($where, $update, array('upsert' => false));
- $results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
- $connection->affected_rows = $results->getModifiedCount();
- return true;
+ return $connection->executeBulkWrite("$db.$table", $bulk, 'getModifiedCount');
}
function delete($table, $queryWhere, $limit = 0) {
@@ -347,9 +380,7 @@ if (isset($_GET["mongo"])) {
$class = 'MongoDB\Driver\BulkWrite';
$bulk = new $class(array());
$bulk->delete($where, array('limit' => $limit));
- $results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
- $connection->affected_rows = $results->getDeletedCount();
- return true;
+ return $connection->executeBulkWrite("$db.$table", $bulk, 'getDeletedCount');
}
function insert($table, $set) {
@@ -357,24 +388,18 @@ if (isset($_GET["mongo"])) {
$db = $connection->_db_name;
$class = 'MongoDB\Driver\BulkWrite';
$bulk = new $class(array());
- if (isset($set['_id']) && empty($set['_id'])) {
+ if ($set['_id'] == '') {
unset($set['_id']);
}
$bulk->insert($set);
- $results = $connection->_link->executeBulkWrite("$db.$table", $bulk);
- $connection->affected_rows = $results->getInsertedCount();
- return true;
+ return $connection->executeBulkWrite("$db.$table", $bulk, 'getInsertedCount');
}
}
function get_databases($flush) {
- /** @var Min_DB */
global $connection;
$return = array();
- $class = 'MongoDB\Driver\Command';
- $command = new $class(array('listDatabases' => 1));
- $results = $connection->_link->executeCommand('admin', $command);
- foreach ($results as $dbs) {
+ foreach ($connection->executeCommand('admin', array('listDatabases' => 1)) as $dbs) {
foreach ($dbs->databases as $db) {
$return[] = $db->name;
}
@@ -389,11 +414,8 @@ if (isset($_GET["mongo"])) {
function tables_list() {
global $connection;
- $class = 'MongoDB\Driver\Command';
- $command = new $class(array('listCollections' => 1));
- $results = $connection->_link->executeCommand($connection->_db_name, $command);
$collections = array();
- foreach ($results as $result) {
+ foreach ($connection->executeCommand($connection->_db_name, array('listCollections' => 1)) as $result) {
$collections[$result->name] = 'table';
}
return $collections;
@@ -406,10 +428,7 @@ if (isset($_GET["mongo"])) {
function indexes($table, $connection2 = null) {
global $connection;
$return = array();
- $class = 'MongoDB\Driver\Command';
- $command = new $class(array('listIndexes' => $table));
- $results = $connection->_link->executeCommand($connection->_db_name, $command);
- foreach ($results as $index) {
+ foreach ($connection->executeCommand($connection->_db_name, array('listIndexes' => $table)) as $index) {
$descs = array();
$columns = array();
foreach (get_object_vars($index->key) as $column => $type) {
@@ -427,24 +446,26 @@ if (isset($_GET["mongo"])) {
}
function fields($table) {
+ global $driver;
$fields = fields_from_edit();
- if (!count($fields)) {
- global $driver;
+ if (!$fields) {
$result = $driver->select($table, array("*"), null, null, array(), 10);
- while ($row = $result->fetch_assoc()) {
- foreach ($row as $key => $val) {
- $row[$key] = null;
- $fields[$key] = array(
- "field" => $key,
- "type" => "string",
- "null" => ($key != $driver->primary),
- "auto_increment" => ($key == $driver->primary),
- "privileges" => array(
- "insert" => 1,
- "select" => 1,
- "update" => 1,
- ),
- );
+ if ($result) {
+ while ($row = $result->fetch_assoc()) {
+ foreach ($row as $key => $val) {
+ $row[$key] = null;
+ $fields[$key] = array(
+ "field" => $key,
+ "type" => "string",
+ "null" => ($key != $driver->primary),
+ "auto_increment" => ($key == $driver->primary),
+ "privileges" => array(
+ "insert" => 1,
+ "select" => 1,
+ "update" => 1,
+ ),
+ );
+ }
}
}
}
@@ -454,16 +475,12 @@ if (isset($_GET["mongo"])) {
function found_rows($table_status, $where) {
global $connection;
$where = where_to_query($where);
- $class = 'MongoDB\Driver\Command';
- $command = new $class(array('count' => $table_status['Name'], 'query' => $where));
- $results = $connection->_link->executeCommand($connection->_db_name, $command);
- $toArray = $results->toArray();
+ $toArray = $connection->executeCommand($connection->_db_name, array('count' => $table_status['Name'], 'query' => $where))->toArray();
return $toArray[0]->n;
}
function sql_query_where_parser($queryWhere) {
- $queryWhere = trim(preg_replace('/WHERE[\s]?[(]?\(?/', '', $queryWhere));
- $queryWhere = preg_replace('/\)\)\)$/', ')', $queryWhere);
+ $queryWhere = preg_replace('~^\sWHERE \(?\(?(.+?)\)?\)?$~', '\1', $queryWhere);
$wheres = explode(' AND ', $queryWhere);
$wheresOr = explode(') OR (', $queryWhere);
$where = array();
@@ -485,10 +502,8 @@ if (isset($_GET["mongo"])) {
if (is_array($where)) {
foreach ($where as $expression) {
list($col, $op, $val) = explode(" ", $expression, 3);
- if ($col == "_id") {
- $val = str_replace('MongoDB\BSON\ObjectID("', "", $val);
- $val = str_replace('")', "", $val);
- $class = 'MongoDB\BSON\ObjectID';
+ if ($col == "_id" && preg_match('~^(MongoDB\\\\BSON\\\\ObjectID)\("(.+)"\)$~', $val, $match)) {
+ list(, $class, $val) = $match;
$val = new $class($val);
}
if (!in_array($op, $adminer->operators)) {
@@ -560,6 +575,7 @@ if (isset($_GET["mongo"])) {
"(date)>=",
"(date)<=",
);
+
}
function table($idf) {
@@ -621,21 +637,11 @@ if (isset($_GET["mongo"])) {
if (($auth_source = getenv("MONGO_AUTH_SOURCE"))) {
$options["authSource"] = $auth_source;
}
- try {
- $connection->_link = $connection->connect("mongodb://$server", $options);
- if ($password != "") {
- $options["password"] = "";
- try {
- $connection->connect("mongodb://$server", $options);
- return lang('Database does not support password.');
- } catch (Exception $ex) {
- // this is what we want
- }
- }
- return $connection;
- } catch (Exception $ex) {
- return $ex->getMessage();
+ $connection->connect("mongodb://$server", $options);
+ if ($connection->error) {
+ return $connection->error;
}
+ return $connection;
}
function alter_indexes($table, $alter) {
@@ -725,8 +731,15 @@ if (isset($_GET["mongo"])) {
return true;
}
- $jush = "mongo";
- $functions = array();
- $grouping = array();
- $edit_functions = array(array("json"));
+ function driver_config() {
+ global $operators;
+ return array(
+ 'possible_drivers' => array("mongo", "mongodb"),
+ 'jush' => "mongo",
+ 'operators' => $operators,
+ 'functions' => array(),
+ 'grouping' => array(),
+ 'edit_functions' => array(array("json")),
+ );
+ }
}
diff --git a/adminer/drivers/mssql.inc.php b/adminer/drivers/mssql.inc.php
index eba5dc52..c8ed37c9 100644
--- a/adminer/drivers/mssql.inc.php
+++ b/adminer/drivers/mssql.inc.php
@@ -8,7 +8,6 @@
$drivers["mssql"] = "MS SQL (beta)";
if (isset($_GET["mssql"])) {
- $possible_drivers = array("SQLSRV", "MSSQL", "PDO_DBLIB");
define("DRIVER", "mssql");
if (extension_loaded("sqlsrv")) {
class Min_DB {
@@ -368,7 +367,7 @@ if (isset($_GET["mssql"])) {
}
function fields($table) {
- $comments = get_key_vals("SELECT objname, cast(value as varchar) FROM fn_listextendedproperty('MS_DESCRIPTION', 'schema', " . q(get_schema()) . ", 'table', " . q($table) . ", 'column', NULL)");
+ $comments = get_key_vals("SELECT objname, cast(value as varchar(max)) FROM fn_listextendedproperty('MS_DESCRIPTION', 'schema', " . q(get_schema()) . ", 'table', " . q($table) . ", 'column', NULL)");
$return = array();
foreach (get_rows("SELECT c.max_length, c.precision, c.scale, c.name, c.is_nullable, c.is_identity, c.collation_name, t.name type, CAST(d.definition as text) [default]
FROM sys.all_columns c
@@ -495,8 +494,8 @@ WHERE OBJECT_NAME(i.object_id) = " . q($table)
}
foreach ($comments as $key => $val) {
$comment = substr($val, 9); // 9 - strlen(" COMMENT ")
- queries("EXEC sp_dropextendedproperty @name = N'MS_Description', @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
- queries("EXEC sp_addextendedproperty @name = N'MS_Description', @value = " . $comment . ", @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
+ queries("EXEC sp_dropextendedproperty @name = N'MS_Description', @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
+ queries("EXEC sp_addextendedproperty @name = N'MS_Description', @value = " . $comment . ", @level0type = N'Schema', @level0name = " . q(get_schema()) . ", @level1type = N'Table', @level1name = " . q($name) . ", @level2type = N'Column', @level2name = " . q($key));
}
return true;
}
@@ -646,28 +645,35 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)
return preg_match('~^(comment|columns|database|drop_col|indexes|descidx|scheme|sql|table|trigger|view|view_trigger)$~', $feature); //! routine|
}
- $jush = "mssql";
- $types = array();
- $structured_types = array();
- foreach (array( //! use sys.types
- lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "int" => 10, "bigint" => 20, "bit" => 1, "decimal" => 0, "real" => 12, "float" => 53, "smallmoney" => 10, "money" => 20),
- lang('Date and time') => array("date" => 10, "smalldatetime" => 19, "datetime" => 19, "datetime2" => 19, "time" => 8, "datetimeoffset" => 10),
- lang('Strings') => array("char" => 8000, "varchar" => 8000, "text" => 2147483647, "nchar" => 4000, "nvarchar" => 4000, "ntext" => 1073741823),
- lang('Binary') => array("binary" => 8000, "varbinary" => 8000, "image" => 2147483647),
- ) as $key => $val) {
- $types += $val;
- $structured_types[$key] = array_keys($val);
+ function driver_config() {
+ $types = array();
+ $structured_types = array();
+ foreach (array( //! use sys.types
+ lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "int" => 10, "bigint" => 20, "bit" => 1, "decimal" => 0, "real" => 12, "float" => 53, "smallmoney" => 10, "money" => 20),
+ lang('Date and time') => array("date" => 10, "smalldatetime" => 19, "datetime" => 19, "datetime2" => 19, "time" => 8, "datetimeoffset" => 10),
+ lang('Strings') => array("char" => 8000, "varchar" => 8000, "text" => 2147483647, "nchar" => 4000, "nvarchar" => 4000, "ntext" => 1073741823),
+ lang('Binary') => array("binary" => 8000, "varbinary" => 8000, "image" => 2147483647),
+ ) as $key => $val) {
+ $types += $val;
+ $structured_types[$key] = array_keys($val);
+ }
+ return array(
+ 'possible_drivers' => array("SQLSRV", "MSSQL", "PDO_DBLIB"),
+ 'jush' => "mssql",
+ 'types' => $types,
+ 'structured_types' => $structured_types,
+ 'unsigned' => array(),
+ 'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"),
+ 'functions' => array("len", "lower", "round", "upper"),
+ 'grouping' => array("avg", "count", "count distinct", "max", "min", "sum"),
+ 'edit_functions' => array(
+ array(
+ "date|time" => "getdate",
+ ), array(
+ "int|decimal|real|float|money|datetime" => "+/-",
+ "char|text" => "+",
+ )
+ ),
+ );
}
- $unsigned = array();
- $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL");
- $functions = array("len", "lower", "round", "upper");
- $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
- $edit_functions = array(
- array(
- "date|time" => "getdate",
- ), array(
- "int|decimal|real|float|money|datetime" => "+/-",
- "char|text" => "+",
- )
- );
}
diff --git a/adminer/drivers/mysql.inc.php b/adminer/drivers/mysql.inc.php
index 379b131c..1ba8d651 100644
--- a/adminer/drivers/mysql.inc.php
+++ b/adminer/drivers/mysql.inc.php
@@ -2,7 +2,6 @@
$drivers = array("server" => "MySQL") + $drivers;
if (!defined("DRIVER")) {
- $possible_drivers = array("MySQLi", "MySQL", "PDO_MySQL");
define("DRIVER", "server"); // server - backwards compatibility
// MySQLi supports everything, MySQL doesn't support multiple result sets, PDO_MySQL doesn't support orgtable
if (extension_loaded("mysqli")) {
@@ -267,7 +266,7 @@ if (!defined("DRIVER")) {
}
function query($query, $unbuffered = false) {
- $this->setAttribute(1000, !$unbuffered); // 1000 - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
+ $this->pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, !$unbuffered);
return parent::query($query, $unbuffered);
}
}
@@ -547,7 +546,7 @@ if (!defined("DRIVER")) {
"type" => $match[1],
"length" => $match[2],
"unsigned" => ltrim($match[3] . $match[4]),
- "default" => ($row["Default"] != "" || preg_match("~char|set~", $match[1]) ? $row["Default"] : null),
+ "default" => ($row["Default"] != "" || preg_match("~char|set~", $match[1]) ? (preg_match('~text~', $match[1]) ? stripslashes(preg_replace("~^'(.*)'\$~", '\1', $row["Default"])) : $row["Default"]) : null),
"null" => ($row["Null"] == "YES"),
"auto_increment" => ($row["Extra"] == "auto_increment"),
"on_update" => (preg_match('~^on update (.+)~i', $row["Extra"], $match) ? $match[1] : ""), //! available since MySQL 5.1.23
@@ -679,17 +678,17 @@ if (!defined("DRIVER")) {
function rename_database($name, $collation) {
$return = false;
if (create_database($name, $collation)) {
- //! move triggers
- $rename = array();
+ $tables = array();
+ $views = array();
foreach (tables_list() as $table => $type) {
- $rename[] = table($table) . " TO " . idf_escape($name) . "." . table($table);
+ if ($type == 'VIEW') {
+ $views[] = $table;
+ } else {
+ $tables[] = $table;
+ }
}
- $return = (!$rename || queries("RENAME TABLE " . implode(", ", $rename)));
- if ($return) {
- queries("DROP DATABASE " . idf_escape(DB));
- }
- restart_session();
- set_session("dbs", null);
+ $return = (!$tables && !$views) || move_tables($tables, $views, $name);
+ drop_databases($return ? array(DB) : array());
}
return $return;
}
@@ -798,12 +797,27 @@ if (!defined("DRIVER")) {
* @return bool
*/
function move_tables($tables, $views, $target) {
+ global $connection;
$rename = array();
- foreach (array_merge($tables, $views) as $table) { // views will report SQL error
+ foreach ($tables as $table) {
$rename[] = table($table) . " TO " . idf_escape($target) . "." . table($table);
}
- return queries("RENAME TABLE " . implode(", ", $rename));
+ if (!$rename || queries("RENAME TABLE " . implode(", ", $rename))) {
+ $definitions = array();
+ foreach ($views as $table) {
+ $definitions[table($table)] = view($table);
+ }
+ $connection->select_db($target);
+ $db = idf_escape(DB);
+ foreach ($definitions as $name => $view) {
+ if (!queries("CREATE VIEW $name AS " . str_replace(" $db.", " ", $view["select"])) || !queries("DROP VIEW $db.$name")) {
+ return false;
+ }
+ }
+ return true;
+ }
//! move triggers
+ return false;
}
/** Copy tables to other schema
@@ -950,7 +964,7 @@ if (!defined("DRIVER")) {
* @return Min_Result
*/
function explain($connection, $query) {
- return $connection->query("EXPLAIN " . (min_version(5.1) ? "PARTITIONS " : "") . $query);
+ return $connection->query("EXPLAIN " . (min_version(5.1) && !min_version(5.7) ? "PARTITIONS " : "") . $query);
}
/** Get approximate number of rows
@@ -1085,7 +1099,8 @@ if (!defined("DRIVER")) {
$return = "CONV($return, 2, 10) + 0";
}
if (preg_match("~geometry|point|linestring|polygon~", $field["type"])) {
- $return = (min_version(8) ? "ST_" : "") . "GeomFromText($return, SRID($field[field]))";
+ $prefix = (min_version(8) ? "ST_" : "");
+ $return = $prefix . "GeomFromText($return, $prefix" . "SRID($field[field]))";
}
return $return;
}
@@ -1098,47 +1113,67 @@ if (!defined("DRIVER")) {
return !preg_match("~scheme|sequence|type|view_trigger|materializedview" . (min_version(8) ? "" : "|descidx" . (min_version(5.1) ? "" : "|event|partitioning" . (min_version(5) ? "" : "|routine|trigger|view"))) . "~", $feature);
}
+ /** Kill a process
+ * @param int
+ * @return bool
+ */
function kill_process($val) {
return queries("KILL " . number($val));
}
+ /** Return query to get connection ID
+ * @return string
+ */
function connection_id(){
return "SELECT CONNECTION_ID()";
}
+ /** Get maximum number of connections
+ * @return int
+ */
function max_connections() {
global $connection;
return $connection->result("SELECT @@max_connections");
}
- $jush = "sql"; ///< @var string JUSH identifier
- $types = array(); ///< @var array ($type => $maximum_unsigned_length, ...)
- $structured_types = array(); ///< @var array ($description => array($type, ...), ...)
- foreach (array(
- lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "mediumint" => 8, "int" => 10, "bigint" => 20, "decimal" => 66, "float" => 12, "double" => 21),
- lang('Date and time') => array("date" => 10, "datetime" => 19, "timestamp" => 19, "time" => 10, "year" => 4),
- lang('Strings') => array("char" => 255, "varchar" => 65535, "tinytext" => 255, "text" => 65535, "mediumtext" => 16777215, "longtext" => 4294967295),
- lang('Lists') => array("enum" => 65535, "set" => 64),
- lang('Binary') => array("bit" => 20, "binary" => 255, "varbinary" => 65535, "tinyblob" => 255, "blob" => 65535, "mediumblob" => 16777215, "longblob" => 4294967295),
- lang('Geometry') => array("geometry" => 0, "point" => 0, "linestring" => 0, "polygon" => 0, "multipoint" => 0, "multilinestring" => 0, "multipolygon" => 0, "geometrycollection" => 0),
- ) as $key => $val) {
- $types += $val;
- $structured_types[$key] = array_keys($val);
+ /** Get driver config
+ * @return array array('possible_drivers' => , 'jush' => , 'types' => , 'structured_types' => , 'unsigned' => , 'operators' => , 'functions' => , 'grouping' => , 'edit_functions' => )
+ */
+ function driver_config() {
+ $types = array(); ///< @var array ($type => $maximum_unsigned_length, ...)
+ $structured_types = array(); ///< @var array ($description => array($type, ...), ...)
+ foreach (array(
+ lang('Numbers') => array("tinyint" => 3, "smallint" => 5, "mediumint" => 8, "int" => 10, "bigint" => 20, "decimal" => 66, "float" => 12, "double" => 21),
+ lang('Date and time') => array("date" => 10, "datetime" => 19, "timestamp" => 19, "time" => 10, "year" => 4),
+ lang('Strings') => array("char" => 255, "varchar" => 65535, "tinytext" => 255, "text" => 65535, "mediumtext" => 16777215, "longtext" => 4294967295),
+ lang('Lists') => array("enum" => 65535, "set" => 64),
+ lang('Binary') => array("bit" => 20, "binary" => 255, "varbinary" => 65535, "tinyblob" => 255, "blob" => 65535, "mediumblob" => 16777215, "longblob" => 4294967295),
+ lang('Geometry') => array("geometry" => 0, "point" => 0, "linestring" => 0, "polygon" => 0, "multipoint" => 0, "multilinestring" => 0, "multipolygon" => 0, "geometrycollection" => 0),
+ ) as $key => $val) {
+ $types += $val;
+ $structured_types[$key] = array_keys($val);
+ }
+ return array(
+ 'possible_drivers' => array("MySQLi", "MySQL", "PDO_MySQL"),
+ 'jush' => "sql", ///< @var string JUSH identifier
+ 'types' => $types,
+ 'structured_types' => $structured_types,
+ 'unsigned' => array("unsigned", "zerofill", "unsigned zerofill"), ///< @var array number variants
+ 'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "REGEXP", "IN", "FIND_IN_SET", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL", "SQL"), ///< @var array operators used in select
+ 'functions' => array("char_length", "date", "from_unixtime", "lower", "round", "floor", "ceil", "sec_to_time", "time_to_sec", "upper"), ///< @var array functions used in select
+ 'grouping' => array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"), ///< @var array grouping functions used in select
+ 'edit_functions' => array( ///< @var array of array("$type|$type2" => "$function/$function2") functions used in editing, [0] - edit and insert, [1] - edit only
+ array(
+ "char" => "md5/sha1/password/encrypt/uuid",
+ "binary" => "md5/sha1",
+ "date|time" => "now",
+ ), array(
+ number_type() => "+/-",
+ "date" => "+ interval/- interval",
+ "time" => "addtime/subtime",
+ "char|text" => "concat",
+ )
+ ),
+ );
}
- $unsigned = array("unsigned", "zerofill", "unsigned zerofill"); ///< @var array number variants
- $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "REGEXP", "IN", "FIND_IN_SET", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL", "SQL"); ///< @var array operators used in select
- $functions = array("char_length", "date", "from_unixtime", "lower", "round", "floor", "ceil", "sec_to_time", "time_to_sec", "upper"); ///< @var array functions used in select
- $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"); ///< @var array grouping functions used in select
- $edit_functions = array( ///< @var array of array("$type|$type2" => "$function/$function2") functions used in editing, [0] - edit and insert, [1] - edit only
- array(
- "char" => "md5/sha1/password/encrypt/uuid",
- "binary" => "md5/sha1",
- "date|time" => "now",
- ), array(
- number_type() => "+/-",
- "date" => "+ interval/- interval",
- "time" => "addtime/subtime",
- "char|text" => "concat",
- )
- );
}
diff --git a/adminer/drivers/oracle.inc.php b/adminer/drivers/oracle.inc.php
index 0395f850..1c5b1598 100644
--- a/adminer/drivers/oracle.inc.php
+++ b/adminer/drivers/oracle.inc.php
@@ -2,11 +2,11 @@
$drivers["oracle"] = "Oracle (beta)";
if (isset($_GET["oracle"])) {
- $possible_drivers = array("OCI8", "PDO_OCI");
define("DRIVER", "oracle");
if (extension_loaded("oci8")) {
class Min_DB {
var $extension = "oci8", $_link, $_result, $server_info, $affected_rows, $errno, $error;
+ var $_current_db;
function _error($errno, $error) {
if (ini_bool("html_errors")) {
@@ -32,6 +32,7 @@ if (isset($_GET["oracle"])) {
}
function select_db($database) {
+ $this->_current_db = $database;
return true;
}
@@ -52,6 +53,7 @@ if (isset($_GET["oracle"])) {
return new Min_Result($result);
}
$this->affected_rows = oci_num_rows($result);
+ oci_free_statement($result);
}
return $return;
}
@@ -119,6 +121,7 @@ if (isset($_GET["oracle"])) {
} elseif (extension_loaded("pdo_oci")) {
class Min_DB extends Min_PDO {
var $extension = "PDO_OCI";
+ var $_current_db;
function connect($server, $username, $password) {
$this->dsn("oci:dbname=//$server;charset=AL32UTF8", $username, $password);
@@ -126,6 +129,7 @@ if (isset($_GET["oracle"])) {
}
function select_db($database) {
+ $this->_current_db = $database;
return true;
}
}
@@ -142,6 +146,25 @@ if (isset($_GET["oracle"])) {
return true; // automatic start
}
+ function insertUpdate($table, $rows, $primary) {
+ global $connection;
+ foreach ($rows as $set) {
+ $update = array();
+ $where = array();
+ foreach ($set as $key => $val) {
+ $update[] = "$key = $val";
+ if (isset($primary[idf_unescape($key)])) {
+ $where[] = "$key = $val";
+ }
+ }
+ if (!(($where && queries("UPDATE " . table($table) . " SET " . implode(", ", $update) . " WHERE " . implode(" AND ", $where)) && $connection->affected_rows)
+ || queries("INSERT INTO " . table($table) . " (" . implode(", ", array_keys($set)) . ") VALUES (" . implode(", ", $set) . ")")
+ )) {
+ return false;
+ }
+ }
+ return true;
+ }
}
@@ -165,7 +188,7 @@ if (isset($_GET["oracle"])) {
}
function get_databases() {
- return get_vals("SELECT tablespace_name FROM user_tablespaces");
+ return get_vals("SELECT tablespace_name FROM user_tablespaces ORDER BY 1");
}
function limit($query, $where, $limit, $offset = 0, $separator = " ") {
@@ -193,22 +216,51 @@ if (isset($_GET["oracle"])) {
return $connection->result("SELECT USER FROM DUAL");
}
+ function get_current_db() {
+ global $connection;
+ $db = $connection->_current_db ? $connection->_current_db : DB;
+ unset($connection->_current_db);
+ return $db;
+ }
+
+ function where_owner($prefix, $owner = "owner") {
+ if (!$_GET["ns"]) {
+ return '';
+ }
+ return "$prefix$owner = sys_context('USERENV', 'CURRENT_SCHEMA')";
+ }
+
+ function views_table($columns) {
+ $owner = where_owner('');
+ return "(SELECT $columns FROM all_views WHERE " . ($owner ? $owner : "rownum < 0") . ")";
+ }
+
function tables_list() {
- return get_key_vals("SELECT table_name, 'table' FROM all_tables WHERE tablespace_name = " . q(DB) . "
-UNION SELECT view_name, 'view' FROM user_views
+ $view = views_table("view_name");
+ $owner = where_owner(" AND ");
+ return get_key_vals("SELECT table_name, 'table' FROM all_tables WHERE tablespace_name = " . q(DB) . "$owner
+UNION SELECT view_name, 'view' FROM $view
ORDER BY 1"
); //! views don't have schema
}
function count_tables($databases) {
- return array();
+ global $connection;
+ $return = array();
+ foreach ($databases as $db) {
+ $return[$db] = $connection->result("SELECT COUNT(*) FROM all_tables WHERE tablespace_name = " . q($db));
+ }
+ return $return;
}
function table_status($name = "") {
$return = array();
$search = q($name);
- foreach (get_rows('SELECT table_name "Name", \'table\' "Engine", avg_row_len * num_rows "Data_length", num_rows "Rows" FROM all_tables WHERE tablespace_name = ' . q(DB) . ($name != "" ? " AND table_name = $search" : "") . "
-UNION SELECT view_name, 'view', 0, 0 FROM user_views" . ($name != "" ? " WHERE view_name = $search" : "") . "
+ $db = get_current_db();
+ $view = views_table("view_name");
+ $owner = where_owner(" AND ");
+ foreach (get_rows('SELECT table_name "Name", \'table\' "Engine", avg_row_len * num_rows "Data_length", num_rows "Rows" FROM all_tables WHERE tablespace_name = ' . q($db) . $owner . ($name != "" ? " AND table_name = $search" : "") . "
+UNION SELECT view_name, 'view', 0, 0 FROM $view" . ($name != "" ? " WHERE view_name = $search" : "") . "
ORDER BY 1"
) as $row) {
if ($name != "") {
@@ -229,11 +281,12 @@ ORDER BY 1"
function fields($table) {
$return = array();
- foreach (get_rows("SELECT * FROM all_tab_columns WHERE table_name = " . q($table) . " ORDER BY column_id") as $row) {
+ $owner = where_owner(" AND ");
+ foreach (get_rows("SELECT * FROM all_tab_columns WHERE table_name = " . q($table) . "$owner ORDER BY column_id") as $row) {
$type = $row["DATA_TYPE"];
$length = "$row[DATA_PRECISION],$row[DATA_SCALE]";
if ($length == ",") {
- $length = $row["DATA_LENGTH"];
+ $length = $row["CHAR_COL_DECL_LENGTH"];
} //! int
$return[$row["COLUMN_NAME"]] = array(
"field" => $row["COLUMN_NAME"],
@@ -254,22 +307,27 @@ ORDER BY 1"
function indexes($table, $connection2 = null) {
$return = array();
- foreach (get_rows("SELECT uic.*, uc.constraint_type
-FROM user_ind_columns uic
-LEFT JOIN user_constraints uc ON uic.index_name = uc.constraint_name AND uic.table_name = uc.table_name
-WHERE uic.table_name = " . q($table) . "
-ORDER BY uc.constraint_type, uic.column_position", $connection2) as $row) {
+ $owner = where_owner(" AND ", "aic.table_owner");
+ foreach (get_rows("SELECT aic.*, ac.constraint_type, atc.data_default
+FROM all_ind_columns aic
+LEFT JOIN all_constraints ac ON aic.index_name = ac.constraint_name AND aic.table_name = ac.table_name AND aic.index_owner = ac.owner
+LEFT JOIN all_tab_cols atc ON aic.column_name = atc.column_name AND aic.table_name = atc.table_name AND aic.index_owner = atc.owner
+WHERE aic.table_name = " . q($table) . "$owner
+ORDER BY ac.constraint_type, aic.column_position", $connection2) as $row) {
$index_name = $row["INDEX_NAME"];
+ $column_name = $row["DATA_DEFAULT"];
+ $column_name = ($column_name ? trim($column_name, '"') : $row["COLUMN_NAME"]); // trim - possibly wrapped in quotes but never contains quotes inside
$return[$index_name]["type"] = ($row["CONSTRAINT_TYPE"] == "P" ? "PRIMARY" : ($row["CONSTRAINT_TYPE"] == "U" ? "UNIQUE" : "INDEX"));
- $return[$index_name]["columns"][] = $row["COLUMN_NAME"];
+ $return[$index_name]["columns"][] = $column_name;
$return[$index_name]["lengths"][] = ($row["CHAR_LENGTH"] && $row["CHAR_LENGTH"] != $row["COLUMN_LENGTH"] ? $row["CHAR_LENGTH"] : null);
- $return[$index_name]["descs"][] = ($row["DESCEND"] ? '1' : null);
+ $return[$index_name]["descs"][] = ($row["DESCEND"] && $row["DESCEND"] == "DESC" ? '1' : null);
}
return $return;
}
function view($name) {
- $rows = get_rows('SELECT text "select" FROM user_views WHERE view_name = ' . q($name));
+ $view = views_table("view_name, text");
+ $rows = get_rows('SELECT text "select" FROM ' . $view . ' WHERE view_name = ' . q($name));
return reset($rows);
}
@@ -294,13 +352,25 @@ ORDER BY uc.constraint_type, uic.column_position", $connection2) as $row) {
function found_rows($table_status, $where) {
}
+ function auto_increment() {
+ return "";
+ }
+
function alter_table($table, $name, $fields, $foreign, $comment, $engine, $collation, $auto_increment, $partitioning) {
$alter = $drop = array();
+ $orig_fields = ($table ? fields($table) : array());
foreach ($fields as $field) {
$val = $field[1];
if ($val && $field[0] != "" && idf_escape($field[0]) != $val[0]) {
queries("ALTER TABLE " . table($table) . " RENAME COLUMN " . idf_escape($field[0]) . " TO $val[0]");
}
+ $orig_field = $orig_fields[$field[0]];
+ if ($val && $orig_field) {
+ $old = process_field($orig_field, $orig_field);
+ if ($val[2] == $old[2]) {
+ $val[2] = "";
+ }
+ }
if ($val) {
$alter[] = ($table != "" ? ($field[0] != "" ? "MODIFY (" : "ADD (") : " ") . implode($val) . ($table != "" ? ")" : ""); //! error with name change only
} else {
@@ -316,6 +386,35 @@ ORDER BY uc.constraint_type, uic.column_position", $connection2) as $row) {
;
}
+ function alter_indexes($table, $alter) {
+ $drop = array();
+ $queries = array();
+ foreach ($alter as $val) {
+ if ($val[0] != "INDEX") {
+ //! descending UNIQUE indexes results in syntax error
+ $val[2] = preg_replace('~ DESC$~', '', $val[2]);
+ $create = ($val[2] == "DROP"
+ ? "\nDROP CONSTRAINT " . idf_escape($val[1])
+ : "\nADD" . ($val[1] != "" ? " CONSTRAINT " . idf_escape($val[1]) : "") . " $val[0] " . ($val[0] == "PRIMARY" ? "KEY " : "") . "(" . implode(", ", $val[2]) . ")"
+ );
+ array_unshift($queries, "ALTER TABLE " . table($table) . $create);
+ } elseif ($val[2] == "DROP") {
+ $drop[] = idf_escape($val[1]);
+ } else {
+ $queries[] = "CREATE INDEX " . idf_escape($val[1] != "" ? $val[1] : uniqid($table . "_")) . " ON " . table($table) . " (" . implode(", ", $val[2]) . ")";
+ }
+ }
+ if ($drop) {
+ array_unshift($queries, "DROP INDEX " . implode(", ", $drop));
+ }
+ foreach ($queries as $query) {
+ if (!queries($query)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
function foreign_keys($table) {
$return = array();
$query = "SELECT c_list.CONSTRAINT_NAME as NAME,
@@ -359,7 +458,8 @@ AND c_src.TABLE_NAME = " . q($table);
}
function schemas() {
- return get_vals("SELECT DISTINCT owner FROM dba_segments WHERE owner IN (SELECT username FROM dba_users WHERE default_tablespace NOT IN ('SYSTEM','SYSAUX'))");
+ $return = get_vals("SELECT DISTINCT owner FROM dba_segments WHERE owner IN (SELECT username FROM dba_users WHERE default_tablespace NOT IN ('SYSTEM','SYSAUX')) ORDER BY 1");
+ return ($return ? $return : get_vals("SELECT DISTINCT owner FROM all_tables WHERE tablespace_name = " . q(DB) . " ORDER BY 1"));
}
function get_schema() {
@@ -401,33 +501,40 @@ ORDER BY PROCESS
}
function support($feature) {
- return preg_match('~^(columns|database|drop_col|indexes|descidx|processlist|scheme|sql|status|table|variables|view|view_trigger)$~', $feature); //!
+ return preg_match('~^(columns|database|drop_col|indexes|descidx|processlist|scheme|sql|status|table|variables|view)$~', $feature); //!
}
- $jush = "oracle";
- $types = array();
- $structured_types = array();
- foreach (array(
- lang('Numbers') => array("number" => 38, "binary_float" => 12, "binary_double" => 21),
- lang('Date and time') => array("date" => 10, "timestamp" => 29, "interval year" => 12, "interval day" => 28), //! year(), day() to second()
- lang('Strings') => array("char" => 2000, "varchar2" => 4000, "nchar" => 2000, "nvarchar2" => 4000, "clob" => 4294967295, "nclob" => 4294967295),
- lang('Binary') => array("raw" => 2000, "long raw" => 2147483648, "blob" => 4294967295, "bfile" => 4294967296),
- ) as $key => $val) {
- $types += $val;
- $structured_types[$key] = array_keys($val);
+ function driver_config() {
+ $types = array();
+ $structured_types = array();
+ foreach (array(
+ lang('Numbers') => array("number" => 38, "binary_float" => 12, "binary_double" => 21),
+ lang('Date and time') => array("date" => 10, "timestamp" => 29, "interval year" => 12, "interval day" => 28), //! year(), day() to second()
+ lang('Strings') => array("char" => 2000, "varchar2" => 4000, "nchar" => 2000, "nvarchar2" => 4000, "clob" => 4294967295, "nclob" => 4294967295),
+ lang('Binary') => array("raw" => 2000, "long raw" => 2147483648, "blob" => 4294967295, "bfile" => 4294967296),
+ ) as $key => $val) {
+ $types += $val;
+ $structured_types[$key] = array_keys($val);
+ }
+ return array(
+ 'possible_drivers' => array("OCI8", "PDO_OCI"),
+ 'jush' => "oracle",
+ 'types' => $types,
+ 'structured_types' => $structured_types,
+ 'unsigned' => array(),
+ 'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL", "SQL"),
+ 'functions' => array("length", "lower", "round", "upper"),
+ 'grouping' => array("avg", "count", "count distinct", "max", "min", "sum"),
+ 'edit_functions' => array(
+ array( //! no parentheses
+ "date" => "current_date",
+ "timestamp" => "current_timestamp",
+ ), array(
+ "number|float|double" => "+/-",
+ "date|timestamp" => "+ interval/- interval",
+ "char|clob" => "||",
+ )
+ ),
+ );
}
- $unsigned = array();
- $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT REGEXP", "NOT IN", "IS NOT NULL", "SQL");
- $functions = array("length", "lower", "round", "upper");
- $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
- $edit_functions = array(
- array( //! no parentheses
- "date" => "current_date",
- "timestamp" => "current_timestamp",
- ), array(
- "number|float|double" => "+/-",
- "date|timestamp" => "+ interval/- interval",
- "char|clob" => "||",
- )
- );
}
diff --git a/adminer/drivers/pgsql.inc.php b/adminer/drivers/pgsql.inc.php
index da4af925..198e6429 100644
--- a/adminer/drivers/pgsql.inc.php
+++ b/adminer/drivers/pgsql.inc.php
@@ -2,7 +2,6 @@
$drivers["pgsql"] = "PostgreSQL";
if (isset($_GET["pgsql"])) {
- $possible_drivers = array("PgSQL", "PDO_PgSQL");
define("DRIVER", "pgsql");
if (extension_loaded("pgsql")) {
class Min_DB {
@@ -41,7 +40,7 @@ if (isset($_GET["pgsql"])) {
}
function value($val, $field) {
- return ($field["type"] == "bytea" ? pg_unescape_bytea($val) : $val);
+ return ($field["type"] == "bytea" && $val !== null ? pg_unescape_bytea($val) : $val);
}
function quoteBinary($string) {
@@ -150,8 +149,7 @@ if (isset($_GET["pgsql"])) {
function connect($server, $username, $password) {
global $adminer;
$db = $adminer->database();
- $string = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' options='-c client_encoding=utf8'";
- $this->dsn("$string dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'", $username, $password);
+ $this->dsn("pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'", $username, $password); //! client_encoding is supported since 9.1 but we can't yet use min_version here
//! connect without DB in case of an error
return true;
}
@@ -286,13 +284,13 @@ if (isset($_GET["pgsql"])) {
function limit1($table, $query, $where, $separator = "\n") {
return (preg_match('~^INTO~', $query)
? limit($query, $where, 1, 0, $separator)
- : " $query" . (is_view(table_status1($table)) ? $where : " WHERE ctid = (SELECT ctid FROM " . table($table) . $where . $separator . "LIMIT 1)")
+ : " $query" . (is_view(table_status1($table)) ? $where : $separator . "WHERE ctid = (SELECT ctid FROM " . table($table) . $where . $separator . "LIMIT 1)")
);
}
function db_collation($db, $collations) {
global $connection;
- return $connection->result("SHOW LC_COLLATE"); //! respect $db
+ return $connection->result("SELECT datcollate FROM pg_database WHERE datname = " . q($db));
}
function engines() {
@@ -306,7 +304,7 @@ if (isset($_GET["pgsql"])) {
function tables_list() {
$query = "SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = current_schema()";
- if (support('materializedview')) {
+ if (support('materializedview')) { // ' - support("materializedview") could be removed by compile.php
$query .= "
UNION ALL
SELECT matviewname, 'MATERIALIZED VIEW'
@@ -327,7 +325,7 @@ ORDER BY 1";
foreach (get_rows("SELECT c.relname AS \"Name\", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'm' THEN 'materialized view' ELSE 'view' END AS \"Engine\", pg_relation_size(c.oid) AS \"Data_length\", pg_total_relation_size(c.oid) - pg_relation_size(c.oid) AS \"Index_length\", obj_description(c.oid, 'pg_class') AS \"Comment\", " . (min_version(12) ? "''" : "CASE WHEN c.relhasoids THEN 'oid' ELSE '' END") . " AS \"Oid\", c.reltuples as \"Rows\", n.nspname
FROM pg_class c
JOIN pg_namespace n ON(n.nspname = current_schema() AND n.oid = c.relnamespace)
-WHERE relkind IN ('r', 'm', 'v', 'f')
+WHERE relkind IN ('r', 'm', 'v', 'f', 'p')
" . ($name != "" ? "AND relname = " . q($name) : "ORDER BY relname")
) as $row) { //! Index_length, Auto_increment
$return[$row["Name"]] = $row;
@@ -350,9 +348,7 @@ WHERE relkind IN ('r', 'm', 'v', 'f')
'timestamp with time zone' => 'timestamptz',
);
- $identity_column = min_version(10) ? "(a.attidentity = 'd')::int" : '0';
-
- foreach (get_rows("SELECT a.attname AS field, format_type(a.atttypid, a.atttypmod) AS full_type, pg_get_expr(d.adbin, d.adrelid) AS default, a.attnotnull::int, col_description(c.oid, a.attnum) AS comment, $identity_column AS identity
+ foreach (get_rows("SELECT a.attname AS field, format_type(a.atttypid, a.atttypmod) AS full_type, pg_get_expr(d.adbin, d.adrelid) AS default, a.attnotnull::int, col_description(c.oid, a.attnum) AS comment" . (min_version(10) ? ", a.attidentity" : "") . "
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
JOIN pg_attribute a ON c.oid = a.attrelid
@@ -375,14 +371,14 @@ ORDER BY a.attnum"
$row["type"] = $type;
$row["full_type"] = $row["type"] . $length . $addon . $array;
}
- if ($row['identity']) {
- $row['default'] = 'GENERATED BY DEFAULT AS IDENTITY';
+ if (in_array($row['attidentity'], array('a', 'd'))) {
+ $row['default'] = 'GENERATED ' . ($row['attidentity'] == 'd' ? 'BY DEFAULT' : 'ALWAYS') . ' AS IDENTITY';
}
$row["null"] = !$row["attnotnull"];
- $row["auto_increment"] = $row['identity'] || preg_match('~^nextval\(~i', $row["default"]);
+ $row["auto_increment"] = $row['attidentity'] || preg_match('~^nextval\(~i', $row["default"]);
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1);
- if (preg_match('~(.+)::[^)]+(.*)~', $row["default"], $match)) {
- $row["default"] = ($match[1] == "NULL" ? null : (($match[1][0] == "'" ? idf_unescape($match[1]) : $match[1]) . $match[2]));
+ if (preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
+ $row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]);
}
$return[$row["field"]] = $row;
}
@@ -397,7 +393,7 @@ ORDER BY a.attnum"
$return = array();
$table_oid = $connection2->result("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($table));
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
- foreach (get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption , (indpred IS NOT NULL)::int as indispartial FROM pg_index i, pg_class ci WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid", $connection2) as $row) {
+ foreach (get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial FROM pg_index i, pg_class ci WHERE i.indrelid = $table_oid AND ci.oid = i.indexrelid", $connection2) as $row) {
$relname = $row["relname"];
$return[$relname]["type"] = ($row["indispartial"] ? "INDEX" : ($row["indisprimary"] ? "PRIMARY" : ($row["indisunique"] ? "UNIQUE" : "INDEX")));
$return[$relname]["columns"] = array();
@@ -422,12 +418,12 @@ WHERE conrelid = (SELECT pc.oid FROM pg_class AS pc INNER JOIN pg_namespace AS p
AND contype = 'f'::char
ORDER BY conkey, conname") as $row) {
if (preg_match('~FOREIGN KEY\s*\((.+)\)\s*REFERENCES (.+)\((.+)\)(.*)$~iA', $row['definition'], $match)) {
- $row['source'] = array_map('trim', explode(',', $match[1]));
+ $row['source'] = array_map('idf_unescape', array_map('trim', explode(',', $match[1])));
if (preg_match('~^(("([^"]|"")+"|[^"]+)\.)?"?("([^"]|"")+"|[^"]+)$~', $match[2], $match2)) {
- $row['ns'] = str_replace('""', '"', preg_replace('~^"(.+)"$~', '\1', $match2[2]));
- $row['table'] = str_replace('""', '"', preg_replace('~^"(.+)"$~', '\1', $match2[4]));
+ $row['ns'] = idf_unescape($match2[2]);
+ $row['table'] = idf_unescape($match2[4]);
}
- $row['target'] = array_map('trim', explode(',', $match[3]));
+ $row['target'] = array_map('idf_unescape', array_map('trim', explode(',', $match[3])));
$row['on_delete'] = (preg_match("~ON DELETE ($on_actions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
$row['on_update'] = (preg_match("~ON UPDATE ($on_actions)~", $match[4], $match2) ? $match2[1] : 'NO ACTION');
$return[$row['conname']] = $row;
@@ -436,9 +432,26 @@ ORDER BY conkey, conname") as $row) {
return $return;
}
+ function constraints($table) {
+ global $on_actions;
+ $return = array();
+ foreach (get_rows("SELECT conname, consrc
+FROM pg_catalog.pg_constraint
+INNER JOIN pg_catalog.pg_namespace ON pg_constraint.connamespace = pg_namespace.oid
+INNER JOIN pg_catalog.pg_class ON pg_constraint.conrelid = pg_class.oid AND pg_constraint.connamespace = pg_class.relnamespace
+WHERE pg_constraint.contype = 'c'
+AND conrelid != 0 -- handle only CONSTRAINTs here, not TYPES
+AND nspname = current_schema()
+AND relname = " . q($table) . "
+ORDER BY connamespace, conname") as $row) {
+ $return[$row['conname']] = $row['consrc'];
+ }
+ return $return;
+ }
+
function view($name) {
global $connection;
- return array("select" => trim($connection->result("SELECT pg_get_viewdef(" . $connection->result("SELECT oid FROM pg_class WHERE relname = " . q($name)) . ")")));
+ return array("select" => trim($connection->result("SELECT pg_get_viewdef(" . $connection->result("SELECT oid FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = current_schema()) AND relname = " . q($name)) . ")")));
}
function collations() {
@@ -492,11 +505,14 @@ ORDER BY conkey, conname") as $row) {
} else {
$val5 = $val[5];
unset($val[5]);
- if (isset($val[6]) && $field[0] == "") { // auto_increment
- $val[1] = ($val[1] == "bigint" ? " big" : " ") . "serial";
- }
if ($field[0] == "") {
+ if (isset($val[6])) { // auto_increment
+ $val[1] = ($val[1] == " bigint" ? " big" : ($val[1] == " smallint" ? " small" : " ")) . "serial";
+ }
$alter[] = ($table != "" ? "ADD " : " ") . implode($val);
+ if (isset($val[6])) {
+ $alter[] = ($table != "" ? "ADD" : " ") . " PRIMARY KEY ($val[0])";
+ }
} else {
if ($column != $val[0]) {
$queries[] = "ALTER TABLE " . table($name) . " RENAME $column TO $val[0]";
@@ -518,7 +534,7 @@ ORDER BY conkey, conname") as $row) {
} elseif ($alter) {
array_unshift($queries, "ALTER TABLE " . table($table) . "\n" . implode(",\n", $alter));
}
- if ($table != "" || $comment != "") {
+ if ($comment !== null) {
$queries[] = "COMMENT ON TABLE " . table($name) . " IS " . q($comment);
}
if ($auto_increment != "") {
@@ -592,21 +608,34 @@ ORDER BY conkey, conname") as $row) {
return true;
}
- function trigger($name, $table = null) {
+ function trigger($name, $table) {
if ($name == "") {
return array("Statement" => "EXECUTE PROCEDURE ()");
}
- if ($table === null) {
- $table = $_GET['trigger'];
+ $columns = array();
+ $where = "WHERE trigger_schema = current_schema() AND event_object_table = " . q($table) . " AND trigger_name = " . q($name);
+ foreach (get_rows("SELECT * FROM information_schema.triggered_update_columns $where") as $row) {
+ $columns[] = $row["event_object_column"];
}
- $rows = get_rows('SELECT t.trigger_name AS "Trigger", t.action_timing AS "Timing", (SELECT STRING_AGG(event_manipulation, \' OR \') FROM information_schema.triggers WHERE event_object_table = t.event_object_table AND trigger_name = t.trigger_name ) AS "Events", t.event_manipulation AS "Event", \'FOR EACH \' || t.action_orientation AS "Type", t.action_statement AS "Statement" FROM information_schema.triggers t WHERE t.event_object_table = ' . q($table) . ' AND t.trigger_name = ' . q($name));
- return reset($rows);
+ $return = array();
+ foreach (get_rows('SELECT trigger_name AS "Trigger", action_timing AS "Timing", event_manipulation AS "Event", \'FOR EACH \' || action_orientation AS "Type", action_statement AS "Statement" FROM information_schema.triggers ' . "$where ORDER BY event_manipulation DESC") as $row) {
+ if ($columns && $row["Event"] == "UPDATE") {
+ $row["Event"] .= " OF";
+ }
+ $row["Of"] = implode(", ", $columns);
+ if ($return) {
+ $row["Event"] .= " OR $return[Event]";
+ }
+ $return = $row;
+ }
+ return $return;
}
function triggers($table) {
$return = array();
- foreach (get_rows("SELECT * FROM information_schema.triggers WHERE event_object_table = " . q($table)) as $row) {
- $return[$row["trigger_name"]] = array($row["action_timing"], $row["event_manipulation"]);
+ foreach (get_rows("SELECT * FROM information_schema.triggers WHERE trigger_schema = current_schema() AND event_object_table = " . q($table)) as $row) {
+ $trigger = trigger($row["trigger_name"], $table);
+ $return[$trigger["Trigger"]] = array($trigger["Timing"], $trigger["Event"]);
}
return $return;
}
@@ -614,7 +643,7 @@ ORDER BY conkey, conname") as $row) {
function trigger_options() {
return array(
"Timing" => array("BEFORE", "AFTER"),
- "Event" => array("INSERT", "UPDATE", "DELETE"),
+ "Event" => array("INSERT", "UPDATE", "UPDATE OF", "DELETE", "INSERT OR UPDATE", "INSERT OR UPDATE OF", "DELETE OR INSERT", "DELETE OR UPDATE", "DELETE OR UPDATE OF", "DELETE OR INSERT OR UPDATE", "DELETE OR INSERT OR UPDATE OF"),
"Type" => array("FOR EACH ROW", "FOR EACH STATEMENT"),
);
}
@@ -704,6 +733,23 @@ AND typelem = 0"
return $return;
}
+ // create_sql() produces CREATE TABLE without FK CONSTRAINTs
+ // foreign_keys_sql() produces all FK CONSTRAINTs as ALTER TABLE ... ADD CONSTRAINT
+ // so that all FKs can be added after all tables have been created, avoiding any need to reorder CREATE TABLE statements in order of their FK dependencies
+ function foreign_keys_sql($table) {
+ $return = "";
+
+ $status = table_status($table);
+ $fkeys = foreign_keys($table);
+ ksort($fkeys);
+
+ foreach ($fkeys as $fkey_name => $fkey) {
+ $return .= "ALTER TABLE ONLY " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " ADD CONSTRAINT " . idf_escape($fkey_name) . " $fkey[definition] " . ($fkey['deferrable'] ? 'DEFERRABLE' : 'NOT DEFERRABLE') . ";\n";
+ }
+
+ return ($return ? "$return\n" : $return);
+ }
+
function create_sql($table, $auto_increment, $style) {
global $connection;
$return = '';
@@ -711,11 +757,14 @@ AND typelem = 0"
$sequences = array();
$status = table_status($table);
+ if (is_view($status)) {
+ $view = view($table);
+ return rtrim("CREATE VIEW " . idf_escape($table) . " AS $view[select]", ";");
+ }
$fields = fields($table);
$indexes = indexes($table);
ksort($indexes);
- $fkeys = foreign_keys($table);
- ksort($fkeys);
+ $constraints = constraints($table);
if (!$status || empty($fields)) {
return false;
@@ -738,7 +787,7 @@ AND typelem = 0"
: "SELECT * FROM $sequence_name"
));
$sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $sequence_name;\n" : "")
- . "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value] START " . ($auto_increment ? $sq['last_value'] : 1) . " CACHE $sq[cache_value];";
+ . "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]" . ($auto_increment && $sq['last_value'] ? " START $sq[last_value]" : "") . " CACHE $sq[cache_value];";
}
}
@@ -755,9 +804,8 @@ AND typelem = 0"
}
}
- // foreign keys
- foreach ($fkeys as $fkey_name => $fkey) {
- $return_parts[] = "CONSTRAINT " . idf_escape($fkey_name) . " $fkey[definition] " . ($fkey['deferrable'] ? 'DEFERRABLE' : 'NOT DEFERRABLE');
+ foreach ($constraints as $conname => $consrc) {
+ $return_parts[] = "CONSTRAINT " . idf_escape($conname) . " CHECK $consrc";
}
$return .= implode(",\n ", $return_parts) . "\n) WITH (oids = " . ($status['Oid'] ? 'true' : 'false') . ");";
@@ -796,7 +844,7 @@ AND typelem = 0"
$return = "";
foreach (triggers($table) as $trg_id => $trg) {
$trigger = trigger($trg_id, $status['Name']);
- $return .= "\nCREATE TRIGGER " . idf_escape($trigger['Trigger']) . " $trigger[Timing] $trigger[Events] ON " . idf_escape($status["nspname"]) . "." . idf_escape($status['Name']) . " $trigger[Type] $trigger[Statement];;\n";
+ $return .= "\nCREATE TRIGGER " . idf_escape($trigger['Trigger']) . " $trigger[Timing] $trigger[Event] ON " . idf_escape($status["nspname"]) . "." . idf_escape($status['Name']) . " $trigger[Type] $trigger[Statement];;\n";
}
return $return;
}
@@ -841,32 +889,39 @@ AND typelem = 0"
return $connection->result("SHOW max_connections");
}
- $jush = "pgsql";
- $types = array();
- $structured_types = array();
- foreach (array( //! arrays
- lang('Numbers') => array("smallint" => 5, "integer" => 10, "bigint" => 19, "boolean" => 1, "numeric" => 0, "real" => 7, "double precision" => 16, "money" => 20),
- lang('Date and time') => array("date" => 13, "time" => 17, "timestamp" => 20, "timestamptz" => 21, "interval" => 0),
- lang('Strings') => array("character" => 0, "character varying" => 0, "text" => 0, "tsquery" => 0, "tsvector" => 0, "uuid" => 0, "xml" => 0),
- lang('Binary') => array("bit" => 0, "bit varying" => 0, "bytea" => 0),
- lang('Network') => array("cidr" => 43, "inet" => 43, "macaddr" => 17, "txid_snapshot" => 0),
- lang('Geometry') => array("box" => 0, "circle" => 0, "line" => 0, "lseg" => 0, "path" => 0, "point" => 0, "polygon" => 0),
- ) as $key => $val) { //! can be retrieved from pg_type
- $types += $val;
- $structured_types[$key] = array_keys($val);
+ function driver_config() {
+ $types = array();
+ $structured_types = array();
+ foreach (array( //! arrays
+ lang('Numbers') => array("smallint" => 5, "integer" => 10, "bigint" => 19, "boolean" => 1, "numeric" => 0, "real" => 7, "double precision" => 16, "money" => 20),
+ lang('Date and time') => array("date" => 13, "time" => 17, "timestamp" => 20, "timestamptz" => 21, "interval" => 0),
+ lang('Strings') => array("character" => 0, "character varying" => 0, "text" => 0, "tsquery" => 0, "tsvector" => 0, "uuid" => 0, "xml" => 0),
+ lang('Binary') => array("bit" => 0, "bit varying" => 0, "bytea" => 0),
+ lang('Network') => array("cidr" => 43, "inet" => 43, "macaddr" => 17, "txid_snapshot" => 0),
+ lang('Geometry') => array("box" => 0, "circle" => 0, "line" => 0, "lseg" => 0, "path" => 0, "point" => 0, "polygon" => 0),
+ ) as $key => $val) { //! can be retrieved from pg_type
+ $types += $val;
+ $structured_types[$key] = array_keys($val);
+ }
+ return array(
+ 'possible_drivers' => array("PgSQL", "PDO_PgSQL"),
+ 'jush' => "pgsql",
+ 'types' => $types,
+ 'structured_types' => $structured_types,
+ 'unsigned' => array(),
+ 'operators' => array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"), // no "SQL" to avoid CSRF
+ 'functions' => array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper"),
+ 'grouping' => array("avg", "count", "count distinct", "max", "min", "sum"),
+ 'edit_functions' => array(
+ array(
+ "char" => "md5",
+ "date|time" => "now",
+ ), array(
+ number_type() => "+/-",
+ "date|time" => "+ interval/- interval", //! escape
+ "char|text" => "||",
+ )
+ ),
+ );
}
- $unsigned = array();
- $operators = array("=", "<", ">", "<=", ">=", "!=", "~", "!~", "LIKE", "LIKE %%", "ILIKE", "ILIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL"); // no "SQL" to avoid CSRF
- $functions = array("char_length", "lower", "round", "to_hex", "to_timestamp", "upper");
- $grouping = array("avg", "count", "count distinct", "max", "min", "sum");
- $edit_functions = array(
- array(
- "char" => "md5",
- "date|time" => "now",
- ), array(
- number_type() => "+/-",
- "date|time" => "+ interval/- interval", //! escape
- "char|text" => "||",
- )
- );
}
diff --git a/adminer/drivers/sqlite.inc.php b/adminer/drivers/sqlite.inc.php
index 4876ebe0..e85fd31c 100644
--- a/adminer/drivers/sqlite.inc.php
+++ b/adminer/drivers/sqlite.inc.php
@@ -3,7 +3,6 @@ $drivers["sqlite"] = "SQLite 3";
$drivers["sqlite2"] = "SQLite 2";
if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
- $possible_drivers = array((isset($_GET["sqlite"]) ? "SQLite3" : "SQLite"), "PDO_SQLite");
define("DRIVER", (isset($_GET["sqlite"]) ? "sqlite" : "sqlite2"));
if (class_exists(isset($_GET["sqlite"]) ? "SQLite3" : "SQLiteDatabase")) {
if (isset($_GET["sqlite"])) {
@@ -141,7 +140,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
}
$return = array();
foreach ($row as $key => $val) {
- $return[($key[0] == '"' ? idf_unescape($key) : $key)] = $val;
+ $return[idf_unescape($key)] = $val;
}
return $return;
}
@@ -191,6 +190,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
if (is_readable($filename) && $this->query("ATTACH " . $this->quote(preg_match("~(^[/\\\\]|:)~", $filename) ? $filename : dirname($_SERVER["SCRIPT_FILENAME"]) . "/$filename") . " AS a")) { // is_readable - SQLite 3
parent::__construct($filename);
$this->query("PRAGMA foreign_keys = 1");
+ $this->query("PRAGMA busy_timeout = 500");
return true;
}
return false;
@@ -676,7 +676,7 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
return array(
"Timing" => strtoupper($match[1]),
"Event" => strtoupper($match[2]) . ($of ? " OF" : ""),
- "Of" => ($of[0] == '`' || $of[0] == '"' ? idf_unescape($of) : $of),
+ "Of" => idf_unescape($of),
"Trigger" => $name,
"Statement" => $match[4],
);
@@ -784,20 +784,26 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
return preg_match('~^(columns|database|drop_col|dump|indexes|descidx|move_col|sql|status|table|trigger|variables|view|view_trigger)$~', $feature);
}
- $jush = "sqlite";
- $types = array("integer" => 0, "real" => 0, "numeric" => 0, "text" => 0, "blob" => 0);
- $structured_types = array_keys($types);
- $unsigned = array();
- $operators = array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"); // REGEXP can be user defined function
- $functions = array("hex", "length", "lower", "round", "unixepoch", "upper");
- $grouping = array("avg", "count", "count distinct", "group_concat", "max", "min", "sum");
- $edit_functions = array(
- array(
- // "text" => "date('now')/time('now')/datetime('now')",
- ), array(
- "integer|real|numeric" => "+/-",
- // "text" => "date/time/datetime",
- "text" => "||",
- )
- );
+ function driver_config() {
+ $types = array("integer" => 0, "real" => 0, "numeric" => 0, "text" => 0, "blob" => 0);
+ return array(
+ 'possible_drivers' => array((isset($_GET["sqlite"]) ? "SQLite3" : "SQLite"), "PDO_SQLite"),
+ 'jush' => "sqlite",
+ 'types' => $types,
+ 'structured_types' => array_keys($types),
+ 'unsigned' => array(),
+ 'operators' => array("=", "<", ">", "<=", ">=", "!=", "LIKE", "LIKE %%", "IN", "IS NULL", "NOT LIKE", "NOT IN", "IS NOT NULL", "SQL"), // REGEXP can be user defined function
+ 'functions' => array("hex", "length", "lower", "round", "unixepoch", "upper"),
+ 'grouping' => array("avg", "count", "count distinct", "group_concat", "max", "min", "sum"),
+ 'edit_functions' => array(
+ array(
+ // "text" => "date('now')/time('now')/datetime('now')",
+ ), array(
+ "integer|real|numeric" => "+/-",
+ // "text" => "date/time/datetime",
+ "text" => "||",
+ )
+ ),
+ );
+ }
}
diff --git a/adminer/dump.inc.php b/adminer/dump.inc.php
index e758f458..e81f80fd 100644
--- a/adminer/dump.inc.php
+++ b/adminer/dump.inc.php
@@ -14,7 +14,7 @@ if ($_POST && !$error) {
$is_sql = preg_match('~sql~', $_POST["format"]);
if ($is_sql) {
- echo "-- Adminer $VERSION " . $drivers[DRIVER] . " dump\n\n";
+ echo "-- Adminer $VERSION " . $drivers[DRIVER] . " " . str_replace("\n", " ", $connection->server_info) . " dump\n\n";
if ($jush == "sql") {
echo "SET NAMES utf8;
SET time_zone = '+00:00';
@@ -22,7 +22,8 @@ SET foreign_key_checks = 0;
" . ($_POST["data_style"] ? "SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
" : "") . "
";
- $connection->query("SET time_zone = '+00:00';");
+ $connection->query("SET time_zone = '+00:00'");
+ $connection->query("SET sql_mode = ''");
}
}
@@ -105,6 +106,16 @@ SET foreign_key_checks = 0;
}
}
+ // add FKs after creating tables (except in MySQL which uses SET FOREIGN_KEY_CHECKS=0)
+ if (function_exists('foreign_keys_sql')) {
+ foreach (table_status('', true) as $name => $table_status) {
+ $table = (DB == "" || in_array($name, (array) $_POST["tables"]));
+ if ($table && !is_view($table_status)) {
+ echo foreign_keys_sql($name);
+ }
+ }
+ }
+
foreach ($views as $view) {
$adminer->dumpTable($view, $_POST["table_style"], 1);
}
diff --git a/adminer/foreign.inc.php b/adminer/foreign.inc.php
index 02392eb0..726d6c79 100644
--- a/adminer/foreign.inc.php
+++ b/adminer/foreign.inc.php
@@ -60,7 +60,7 @@ if ($row["ns"] != "") {
set_schema($row["ns"]);
}
$referencable = array_keys(array_filter(table_status('', true), 'fk_support'));
-$target = ($TABLE === $row["table"] ? $source : array_keys(fields(in_array($row["table"], $referencable) ? $row["table"] : reset($referencable))));
+$target = array_keys(fields(in_array($row["table"], $referencable) ? $row["table"] : reset($referencable)));
$onchange = "this.form['change-js'].value = '1'; this.form.submit();";
echo "" . lang('Target table') . ": " . html_select("table", $referencable, $row["table"], $onchange) . "\n";
if ($jush == "pgsql") {
diff --git a/adminer/include/adminer.inc.php b/adminer/include/adminer.inc.php
index d8044e5f..632e2928 100644
--- a/adminer/include/adminer.inc.php
+++ b/adminer/include/adminer.inc.php
@@ -480,7 +480,7 @@ class Adminer {
echo "\n";
echo "\n";
}
-
+
/** Print command box in select
* @return bool whether to print default commands
*/
@@ -563,6 +563,7 @@ class Adminer {
foreach ($fields as $name => $field) {
if ((preg_match('~^[-\d.' . (preg_match('~IN$~', $val["op"]) ? ',' : '') . ']+$~', $val["val"]) || !preg_match('~' . number_type() . '|bit~', $field["type"]))
&& (!preg_match("~[\x80-\xFF]~", $val["val"]) || preg_match('~char|text|enum|set~', $field["type"]))
+ && (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val["val"]))
) {
$cols[] = $prefix . $driver->convertSearch(idf_escape($name), $val, $field) . $cond;
}
@@ -658,6 +659,16 @@ class Adminer {
;
}
+ /** Print before edit form
+ * @param string
+ * @param array
+ * @param mixed
+ * @param bool
+ * @return null
+ */
+ function editRowPrint($table, $fields, $row, $update) {
+ }
+
/** Functions displayed in edit form
* @param array single field from fields()
* @return array
@@ -665,19 +676,20 @@ class Adminer {
function editFunctions($field) {
global $edit_functions;
$return = ($field["null"] ? "NULL/" : "");
+ $update = isset($_GET["select"]) || where($_GET);
foreach ($edit_functions as $key => $functions) {
- if (!$key || (!isset($_GET["call"]) && (isset($_GET["select"]) || where($_GET)))) { // relative functions
+ if (!$key || (!isset($_GET["call"]) && $update)) { // relative functions
foreach ($functions as $pattern => $val) {
if (!$pattern || preg_match("~$pattern~", $field["type"])) {
$return .= "/$val";
}
}
- if ($key && !preg_match('~set|blob|bytea|raw|file~', $field["type"])) {
- $return .= "/SQL";
- }
+ }
+ if ($key && !preg_match('~set|blob|bytea|raw|file|bool~', $field["type"])) {
+ $return .= "/SQL";
}
}
- if ($field["auto_increment"] && !isset($_GET["select"]) && !where($_GET)) {
+ if ($field["auto_increment"] && !$update) {
$return = lang('Auto Increment');
}
return explode("/", $return);
@@ -944,6 +956,7 @@ class Adminer {
echo "
\n" . script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});");
}
} else {
+ $tables = array();
if ($_GET["ns"] !== "" && !$missing && DB != "") {
$connection->select_db(DB);
$tables = table_status('', true);
@@ -998,7 +1011,7 @@ bodyLoad('databases();
- if ($databases && !in_array(DB, $databases)) {
+ if (DB && $databases && !in_array(DB, $databases)) {
array_unshift($databases, DB);
}
?>
@@ -1012,8 +1025,8 @@ bodyLoad('\n"
);
echo " \n";
- if ($missing != "db" && DB != "" && $connection->select_db(DB)) {
- if (support("scheme")) {
+ if (support("scheme")) {
+ if ($missing != "db" && DB != "" && $connection->select_db(DB)) {
echo " " . lang('Schema') . ": " . optionlist(array("" => "") + $adminer->schemas(), $_GET["ns"]) . " $db_events";
if ($_GET["ns"] != "") {
set_schema($_GET["ns"]);
@@ -1038,7 +1051,10 @@ bodyLoad(' $status) {
$name = $this->tableName($status);
if ($name != "") {
- echo '" . lang('select') . " ";
+ echo '" . lang('select') . " "
+ ;
echo (support("table") || support("indexes")
? 'operators === null) {
- $adminer->operators = $operators;
-}
diff --git a/adminer/include/auth.inc.php b/adminer/include/auth.inc.php
index f8a5a399..ed104154 100644
--- a/adminer/include/auth.inc.php
+++ b/adminer/include/auth.inc.php
@@ -41,7 +41,7 @@ function add_invalid_login() {
function check_invalid_login() {
global $adminer;
$invalids = unserialize(@file_get_contents(get_temp_dir() . "/adminer.invalid")); // @ - may not exist
- $invalid = $invalids[$adminer->bruteForceKey()];
+ $invalid = ($invalids ? $invalids[$adminer->bruteForceKey()] : array());
$next_attempt = ($invalid[1] > 29 ? $invalid[0] - time() : 0); // allow 30 invalid attempts
if ($next_attempt > 0) { //! do the same with permanent login
auth_error(lang('Too many unsuccessful logins, try again in %d minute(s).', ceil($next_attempt / 60)));
@@ -73,18 +73,12 @@ if ($auth) {
redirect(auth_url($vendor, $server, $username, $db));
}
-} elseif ($_POST["logout"]) {
- if ($has_token && !verify_token()) {
- page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.'));
- page_footer("db");
- exit;
- } else {
- foreach (array("pwds", "db", "dbs", "queries") as $key) {
- set_session($key, null);
- }
- unset_permanent();
- redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1), lang('Logout successful.') . ' ' . lang('Thanks for using Adminer, consider donating .'));
+} elseif ($_POST["logout"] && (!$has_token || verify_token())) {
+ foreach (array("pwds", "db", "dbs", "queries") as $key) {
+ set_session($key, null);
}
+ unset_permanent();
+ redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~', '', ME), 0, -1), lang('Logout successful.') . ' ' . lang('Thanks for using Adminer, consider donating .'));
} elseif ($permanent && !$_SESSION["pwds"]) {
session_regenerate_id();
@@ -125,7 +119,7 @@ function auth_error($error) {
$password = get_password();
if ($password !== null) {
if ($password === false) {
- $error .= ' ' . lang('Master password expired. Implement %s method to make it permanent.', target_blank(), 'permanentLogin()
');
+ $error .= ($error ? ' ' : '') . lang('Master password expired. Implement %s method to make it permanent.', target_blank(), 'permanentLogin()
');
}
set_password(DRIVER, SERVER, $_GET["username"], null);
}
@@ -162,7 +156,7 @@ stop_session(true);
if (isset($_GET["username"]) && is_string(get_password())) {
list($host, $port) = explode(":", SERVER, 2);
- if (is_numeric($port) && $port < 1024) {
+ if (preg_match('~^\s*([-+]?\d+)~', $port, $match) && ($match[1] < 1024 || $match[1] > 65535)) { // is_numeric('80#') would still connect to port 80
auth_error(lang('Connecting to privileged ports is not allowed.'));
}
check_invalid_login();
@@ -176,6 +170,12 @@ if (!is_object($connection) || ($login = $adminer->login($_GET["username"], get_
auth_error($error . (preg_match('~^ | $~', get_password()) ? ' ' . lang('There is a space in the input password which might be the cause.') : ''));
}
+if ($_POST["logout"] && $has_token && !verify_token()) {
+ page_header(lang('Logout'), lang('Invalid CSRF token. Send the form again.'));
+ page_footer("db");
+ exit;
+}
+
if ($auth && $_POST["token"]) {
$_POST["token"] = $token; // reset token after explicit login
}
diff --git a/adminer/include/bootstrap.inc.php b/adminer/include/bootstrap.inc.php
index 00baf919..8eaff399 100644
--- a/adminer/include/bootstrap.inc.php
+++ b/adminer/include/bootstrap.inc.php
@@ -1,5 +1,10 @@
operators === null) {
+ $adminer->operators = $operators;
+}
+
define("SERVER", $_GET[DRIVER]); // read from pgsql=localhost
define("DB", $_GET["db"]); // for the sake of speed and size
-define("ME", str_replace(":", "%3a", preg_replace('~^[^?]*/([^?]*).*~', '\1', $_SERVER["REQUEST_URI"])) . '?'
+define("ME", preg_replace('~\?.*~', '', relative_uri()) . '?'
. (sid() ? SID . '&' : '')
. (SERVER !== null ? DRIVER . "=" . urlencode(SERVER) . '&' : '')
. (isset($_GET["username"]) ? "username=" . urlencode($_GET["username"]) . '&' : '')
@@ -92,7 +110,6 @@ define("ME", str_replace(":", "%3a", preg_replace('~^[^?]*/([^?]*).*~', '\1', $_
);
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";
diff --git a/adminer/include/connect.inc.php b/adminer/include/connect.inc.php
index a3e188fa..38f477c8 100644
--- a/adminer/include/connect.inc.php
+++ b/adminer/include/connect.inc.php
@@ -89,14 +89,16 @@ if (!(DB != "" ? $connection->select_db(DB) : isset($_GET["sql"]) || isset($_GET
exit;
}
-if (support("scheme") && DB != "" && $_GET["ns"] !== "") {
- if (!isset($_GET["ns"])) {
- redirect(preg_replace('~ns=[^&]*&~', '', ME) . "ns=" . get_schema());
- }
- if (!set_schema($_GET["ns"])) {
- header("HTTP/1.1 404 Not Found");
- page_header(lang('Schema') . ": " . h($_GET["ns"]), lang('Invalid schema.'), true);
- page_footer("ns");
- exit;
+if (support("scheme")) {
+ if (DB != "" && $_GET["ns"] !== "") {
+ if (!isset($_GET["ns"])) {
+ redirect(preg_replace('~ns=[^&]*&~', '', ME) . "ns=" . get_schema());
+ }
+ if (!set_schema($_GET["ns"])) {
+ header("HTTP/1.1 404 Not Found");
+ page_header(lang('Schema') . ": " . h($_GET["ns"]), lang('Invalid schema.'), true);
+ page_footer("ns");
+ exit;
+ }
}
}
diff --git a/adminer/include/design.inc.php b/adminer/include/design.inc.php
index 544fb5fa..09c3a854 100644
--- a/adminer/include/design.inc.php
+++ b/adminer/include/design.inc.php
@@ -75,7 +75,7 @@ var thousandsSeparator = '';
if ($breadcrumb === false) {
echo "$server\n";
} else {
- echo "$server » ";
+ echo "$server » ";
if ($_GET["ns"] != "" || (DB != "" && is_array($breadcrumb))) {
echo '' . h(DB) . ' » ';
}
diff --git a/adminer/include/driver.inc.php b/adminer/include/driver.inc.php
index 893d1667..99582bd4 100644
--- a/adminer/include/driver.inc.php
+++ b/adminer/include/driver.inc.php
@@ -1,4 +1,15 @@
";
foreach ($row as $key => $val) {
+ $link = "";
+ if (isset($links[$key]) && !$columns[$links[$key]]) {
+ if ($orgtables && $jush == "sql") { // MySQL EXPLAIN
+ $table = $row[array_search("table=", $links)];
+ $link = ME . $links[$key] . urlencode($orgtables[$table] != "" ? $orgtables[$table] : $table);
+ } else {
+ $link = ME . "edit=" . urlencode($links[$key]);
+ foreach ($indexes[$links[$key]] as $col => $j) {
+ $link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]);
+ }
+ }
+ } elseif (is_url($val)) {
+ $link = $val;
+ }
if ($val === null) {
$val = "NULL ";
} elseif ($blobs[$key] && !is_utf8($val)) {
@@ -71,17 +85,8 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
$val = "$val
";
}
}
- if (isset($links[$key]) && !$columns[$links[$key]]) {
- if ($orgtables && $jush == "sql") { // MySQL EXPLAIN
- $table = $row[array_search("table=", $links)];
- $link = $links[$key] . urlencode($orgtables[$table] != "" ? $orgtables[$table] : $table);
- } else {
- $link = "edit=" . urlencode($links[$key]);
- foreach ($indexes[$links[$key]] as $col => $j) {
- $link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]);
- }
- }
- $val = "$val ";
+ if ($link) {
+ $val = "$val ";
}
echo "$val";
}
@@ -146,7 +151,7 @@ function set_adminer_settings($settings) {
*/
function textarea($name, $value, $rows = 10, $cols = 80) {
global $jush;
- echo "