1
0
mirror of https://github.com/vrana/adminer.git synced 2025-09-03 11:22:35 +02:00

Compare commits

..

45 Commits
v4.9 ... v4.9.3

Author SHA1 Message Date
Peter Knut
507f335371 Release 4.9.3 2024-10-02 17:07:09 +02:00
Peter Knut
ea314b8103 Hide invalid edit form if table record is not found 2024-10-02 09:35:59 +02:00
Peter Knut
e250470768 PostgreSQL: Fix editing record that contains a field with GENERATED ALWAYS default value
Fields with GENERATED ALWAYS default values are also disabled.

Thanks to PurpleTape (https://github.com/adminerevo/adminerevo/issues/201).
2024-10-02 00:29:24 +02:00
Peter Knut
2fa42d50eb Fix using undefined Min_DB::info property 2024-10-01 23:37:07 +02:00
Peter Knut
a366b7af09 MySQL: Fix editing user's proxy privilege, refactoring
- Uncheck all other priviledges if 'All privileges' is checked.
- Refactor related functions.
2024-10-01 23:22:26 +02:00
Peter Knut
b039a39e4d Bigger font size for code blocks 2024-10-01 09:07:21 +02:00
SeaEagle
08e48c8641 MySQL: Fix where clause for JSON column
Issue: https://github.com/adminerevo/adminerevo/issues/175
2024-10-01 09:06:20 +02:00
Peter Knut
78c2041cfd Fix background color of <pre> used as edit field 2024-10-01 00:33:47 +02:00
Peter Knut
5d7c5fa268 Do not include unchanged PARTITION BY definition into ALTER TABLE query 2024-09-22 00:33:55 +02:00
Peter Knut
8f1db4cf6f Add helper methods for dumping variable to the output 2024-09-21 22:34:38 +02:00
Peter Knut
9daa88acca MariaDB: Fix comparing CURRENT_TIMESTAMP definition while altering a table 2024-09-21 22:20:08 +02:00
Peter Knut
aa519b78ca MySQL, PostgreSQL: Fix queries splitting and string constants
Thanks to alxivnov (https://github.com/vrana/adminer/pull/490).
2024-09-21 09:28:50 +02:00
Michael Graß
aee800efed Do not limit unlimited memory, fix number conversion warning 2024-09-20 22:28:46 +02:00
Peter Knut
06d0f957d5 Bump version to 4.9.3-dev 2024-09-18 10:57:48 +02:00
Peter Knut
3f0bc24e01 Release 4.9.2 2024-09-18 09:50:35 +02:00
Peter Knut
4863f48d33 Basic JS code cleanup 2024-09-18 09:39:17 +02:00
Peter Knut
9ea8f44919 Fix undefined property in error message if driver does not support error number 2024-09-18 00:46:34 +02:00
khoazero123
fa791b5461 PostgreSQL: Fix exporting CREATE TABLE with sequence default value 2024-09-18 00:31:15 +02:00
Roy-Orbison
203162b203 Function to retrieve driver name
Plugins cannot access $drivers global after compilation.
2024-09-18 00:27:02 +02:00
Peter Knut
e4e76b6384 PostgreSQL: Allow to set connection's sslmode with AdminerLoginSsl plugin
Thanks to wodka (https://github.com/vrana/adminer/pull/427/files)
2024-09-18 00:27:02 +02:00
Peter Knut
353cd452a3 PostgreSQL: Fix exporting CREATE TABLE query with GENERATED default values
Thanks to GottfriedCP (https://github.com/adminerevo/adminerevo/issues/157)
2024-09-18 00:27:02 +02:00
Peter Knut
5bc4ac6c18 Merge branch 'editor-search-fix' 2024-09-17 15:46:51 +02:00
Peter Knut
aec8275502 Editor: Fix searching in tables
Thanks to ytetsuro (https://github.com/vrana/adminer/pull/473)
2024-09-17 15:46:36 +02:00
Peter Knut
7d5077e687 Cleanup the code for searching 2024-09-17 15:36:02 +02:00
Peter Knut
91d0d8538f MySQL: Do not show 'empty' enum value in strict mode 2024-09-10 23:47:06 +02:00
Peter Knut
2439369143 PostreSQL: Fix search condition for network address types, add macaddr8 type
This fixes issue https://github.com/adminerevo/adminerevo/issues/115
2024-09-10 10:27:59 +02:00
Peter Knut
d5bce9b3e9 PostreSQL: Fix search fields configuration
Regression from 4.9.
2024-09-10 08:28:18 +02:00
Peter Knut
b42762e4dc Remove hardcoded textarea height 2024-09-09 23:54:51 +02:00
Peter Knut
695a720403 Bump version to 4.9.2-dev 2024-09-09 23:53:50 +02:00
Peter Knut
e6fdf2b400 Release 4.9.1 2024-09-09 10:30:14 +02:00
Peter Knut
b542b6613c PostgreSQL: Fix undefined properties on PHP 8
Thanks to FrancoisCapon (https://github.com/vrana/adminer/pull/429)
2024-09-08 23:22:44 +02:00
Peter Knut
374b8ed6a6 PostgreSQL: Fix documentation link for SERIAL type
Thanks to leggiero (https://github.com/vrana/adminer/pull/432)
2024-09-08 23:01:47 +02:00
Michal Paulovic
58cca3f951 MySQL: Add unix_timestamp to functions 2024-09-08 23:01:46 +02:00
caltong
5eecb8e6a3 PostgreSQL: Make data length calculation more accurate 2024-09-08 22:28:06 +02:00
Thomas Daniels
0d0936550c PostgreSQL: Show only accessible databases 2024-09-08 01:02:49 +02:00
Peter Knut
c4ed9500a1 Add support for translations in plugins 2024-09-07 22:54:04 +02:00
Peter Knut
0863766970 Replace deprecated <acronym> with <abbr> 2024-09-07 22:53:18 +02:00
Peter Knut
146a24efad AdminerLoginOtp: Autocomplete hints for OTP input field, code refactoring
Tanks to SGCBB (https://github.com/vrana/adminer/pull/488)
2024-09-07 22:53:08 +02:00
Sneda8
00b9fbda08 PHP 8.3 error suppression
PHP 8.3 has shortened the array access on null error message to "Trying to access array offset on null". This commit changes the regular expression used to circumvent errors.
2024-09-06 00:33:53 +02:00
Peter Knut
8ea329538f Improved displaying of long table names in menu 2024-09-03 00:34:00 +02:00
Peter Knut
a3428cc7ff Fix compiling jush external files 2024-09-02 23:18:44 +02:00
Peter Knut
2a01969c96 Add .editorconfig file
Thanks to cweiske (https://github.com/adminerevo/adminerevo/pull/163).
2024-08-26 00:56:10 +02:00
Peter Knut
9b8d14c3ee Refactor and fix the plugin AdminerEditForeign
Thanks to Amunak (https://github.com/adminerevo/adminerevo/pull/86).
2024-08-25 23:48:02 +02:00
Peter Knut
2ce88d9bdc Fix field selection in Elasticsearch
Thanks to cweiske: https://github.com/adminerevo/adminerevo/pull/159
2024-08-25 22:14:50 +02:00
Peter Knut
593c8e5bcc Bump version to 4.9.1-dev 2024-08-21 00:07:57 +02:00
77 changed files with 1502 additions and 727 deletions

20
.editorconfig Normal file
View File

@@ -0,0 +1,20 @@
# https://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{php,css,js}]
indent_style = tab
[*.json]
indent_style = space
indent_size = 4
[*.md]
indent_style = space
trim_trailing_whitespace = false
max_line_length = 120

View File

@@ -82,21 +82,40 @@ if ($_POST && !process_fields($row["fields"]) && !$error) {
}
$partitioning = "";
if ($partition_by[$row["partition_by"]]) {
$partitions = array();
if ($row["partition_by"] == 'RANGE' || $row["partition_by"] == 'LIST') {
foreach (array_filter($row["partition_names"]) as $key => $val) {
$value = $row["partition_values"][$key];
$partitions[] = "\n PARTITION " . idf_escape($val) . " VALUES " . ($row["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
if (support("partitioning")) {
if (isset($partition_by[$row["partition_by"]])) {
$params = array_filter($row, function ($key) {
return preg_match('~^partition~', $key);
}, ARRAY_FILTER_USE_KEY);
foreach ($params["partition_names"] as $key => $name) {
if ($name === "") {
unset($params["partition_names"][$key]);
unset($params["partition_values"][$key]);
}
}
$partitioning .= "\nPARTITION BY $row[partition_by]($row[partition])" . ($partitions // $row["partition"] can be expression, not only column
? " (" . implode(",", $partitions) . "\n)"
: ($row["partitions"] ? " PARTITIONS " . (+$row["partitions"]) : "")
);
} elseif (support("partitioning") && preg_match("~partitioned~", $table_status["Create_options"])) {
if ($params != get_partitions_info($TABLE)) {
$partitions = [];
if ($params["partition_by"] == 'RANGE' || $params["partition_by"] == 'LIST') {
foreach ($params["partition_names"] as $key => $name) {
$value = $params["partition_values"][$key];
$partitions[] = "\n PARTITION " . idf_escape($name) . " VALUES " . ($params["partition_by"] == 'RANGE' ? "LESS THAN" : "IN") . ($value != "" ? " ($value)" : " MAXVALUE"); //! SQL injection
}
}
// $params["partition"] can be expression, not only column
$partitioning .= "\nPARTITION BY {$params["partition_by"]}({$params["partition"]})";
if ($partitions) {
$partitioning .= " (" . implode(",", $partitions) . "\n)";
} elseif ($params["partitions"]) {
$partitioning .= " PARTITIONS " . (int)$params["partitions"];
}
}
} elseif (preg_match("~partitioned~", $table_status["Create_options"])) {
$partitioning .= "\nREMOVE PARTITIONING";
}
}
$message = lang('Table has been altered.');
if ($TABLE == "") {
@@ -141,13 +160,9 @@ if (!$_POST) {
}
if (support("partitioning")) {
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($TABLE);
$result = $connection->query("SELECT PARTITION_METHOD, PARTITION_ORDINAL_POSITION, PARTITION_EXPRESSION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
list($row["partition_by"], $row["partitions"], $row["partition"]) = $result->fetch_row();
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
$partitions[""] = "";
$row["partition_names"] = array_keys($partitions);
$row["partition_values"] = array_values($partitions);
$row += get_partitions_info($TABLE);
$row["partition_names"][] = "";
$row["partition_values"][] = "";
}
}
}

View File

@@ -630,6 +630,17 @@ WHERE sys1.xtype = 'TR' AND sys2.name = " . q($table)
return array();
}
/**
* @return bool
*/
function is_strict_mode() {
return false;
}
function is_c_style_escapes() {
return true;
}
function show_status() {
return array();
}

View File

@@ -16,10 +16,17 @@ if (!defined("DRIVER")) {
global $adminer;
mysqli_report(MYSQLI_REPORT_OFF); // stays between requests, not required since PHP 5.3.4
list($host, $port) = explode(":", $server, 2); // part after : is used for port or socket
$ssl = $adminer->connectSsl();
if ($ssl) {
$this->ssl_set($ssl['key'], $ssl['cert'], $ssl['ca'], '', '');
if (isset($ssl['key']) || isset($ssl['cert']) || isset($ssl['ca'])) {
$this->ssl_set(
isset($ssl['key']) ? $ssl['key'] : null,
isset($ssl['cert']) ? $ssl['cert'] : null,
isset($ssl['ca']) ? $ssl['ca'] : null,
null, null
);
}
$return = @$this->real_connect(
($server != "" ? $host : ini_get("mysqli.default_host")),
($server . $username != "" ? $username : ini_get("mysqli.default_user")),
@@ -234,25 +241,23 @@ if (!defined("DRIVER")) {
function connect($server, $username, $password) {
global $adminer;
$options = array(PDO::MYSQL_ATTR_LOCAL_INFILE => false);
$dsn = "mysql:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server));
$options = [PDO::MYSQL_ATTR_LOCAL_INFILE => false];
$ssl = $adminer->connectSsl();
if ($ssl) {
if (!empty($ssl['key'])) {
if (isset($ssl['key'])) {
$options[PDO::MYSQL_ATTR_SSL_KEY] = $ssl['key'];
}
if (!empty($ssl['cert'])) {
if (isset($ssl['cert'])) {
$options[PDO::MYSQL_ATTR_SSL_CERT] = $ssl['cert'];
}
if (!empty($ssl['ca'])) {
if (isset($ssl['ca'])) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $ssl['ca'];
}
}
$this->dsn(
"mysql:charset=utf8;host=" . str_replace(":", ";unix_socket=", preg_replace('~:(\d)~', ';port=\1', $server)),
$username,
$password,
$options
);
$this->dsn($dsn, $username, $password, $options);
return true;
}
@@ -316,8 +321,8 @@ if (!defined("DRIVER")) {
}
}
function convertSearch($idf, $val, $field) {
return (preg_match('~char|text|enum|set~', $field["type"]) && !preg_match("~^utf8~", $field["collation"]) && preg_match('~[\x80-\xFF]~', $val['val'])
function convertSearch($idf, array $where, array $field) {
return (preg_match('~char|text|enum|set~', $field["type"]) && !preg_match("~^utf8~", $field["collation"]) && preg_match('~[\x80-\xFF]~', $where['val'])
? "CONVERT($idf USING " . charset($this->_conn) . ")"
: $idf
);
@@ -1056,6 +1061,29 @@ if (!defined("DRIVER")) {
return get_key_vals("SHOW VARIABLES");
}
/**
* @return bool
*/
function is_strict_mode() {
static $strictMode = null;
if ($strictMode === null) {
$strictMode = (bool)preg_match('~STRICT_(TRANS|ALL)_TABLES~', get_key_vals("SHOW VARIABLES LIKE 'sql_mode'")["sql_mode"]);
}
return $strictMode;
}
function is_c_style_escapes() {
static $c_style = null;
if ($c_style === null) {
$c_style = strpos(get_key_vals("SHOW VARIABLES LIKE 'sql_mode'")["sql_mode"], 'NO_BACKSLASH_ESCAPES') === false;
}
return $c_style;
}
/** Get process list
* @return array ($row)
*/
@@ -1160,7 +1188,7 @@ if (!defined("DRIVER")) {
'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
'functions' => array("char_length", "date", "from_unixtime", "unix_timestamp", "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(

View File

@@ -486,6 +486,17 @@ AND c_src.TABLE_NAME = " . q($table);
return get_key_vals('SELECT name, display_value FROM v$parameter');
}
/**
* @return bool
*/
function is_strict_mode() {
return false;
}
function is_c_style_escapes() {
return true;
}
function process_list() {
return get_rows('SELECT sess.process AS "process", sess.username AS "user", sess.schemaname AS "schema", sess.status AS "status", sess.wait_class AS "wait_class", sess.seconds_in_wait AS "seconds_in_wait", sql.sql_text AS "sql_text", sess.machine AS "machine", sess.port AS "port"
FROM v$session sess LEFT OUTER JOIN v$sql sql

View File

@@ -19,7 +19,14 @@ if (isset($_GET["pgsql"])) {
global $adminer;
$db = $adminer->database();
set_error_handler(array($this, '_error'));
$this->_string = "host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' user='" . addcslashes($username, "'\\") . "' password='" . addcslashes($password, "'\\") . "'";
$ssl = $adminer->connectSsl();
if (isset($ssl["mode"])) {
$this->_string .= " sslmode='" . $ssl["mode"] . "'";
}
$this->_link = @pg_connect("$this->_string dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'", PGSQL_CONNECT_FORCE_NEW);
if (!$this->_link && $db != "") {
// try to connect directly with database for performance
@@ -36,7 +43,7 @@ if (isset($_GET["pgsql"])) {
}
function quote($string) {
return "'" . pg_escape_string($this->_link, $string) . "'";
return pg_escape_literal($this->_link, $string);
}
function value($val, $field) {
@@ -148,9 +155,19 @@ if (isset($_GET["pgsql"])) {
function connect($server, $username, $password) {
global $adminer;
$db = $adminer->database();
$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
//! client_encoding is supported since 9.1, but we can't yet use min_version here
$dsn = "pgsql:host='" . str_replace(":", "' port='", addcslashes($server, "'\\")) . "' client_encoding=utf8 dbname='" . ($db != "" ? addcslashes($db, "'\\") : "postgres") . "'";
$ssl = $adminer->connectSsl();
if (isset($ssl["mode"])) {
$dsn .= " sslmode='" . $ssl["mode"] . "'";
}
$this->dsn($dsn, $username, $password);
return true;
}
@@ -212,13 +229,13 @@ if (isset($_GET["pgsql"])) {
return $query;
}
function convertSearch($idf, $val, $field) {
return (preg_match('~char|text'
. (!preg_match('~LIKE~', $val["op"]) ? '|date|time(stamp)?|boolean|uuid|' . number_type() : '')
. '~', $field["type"])
? $idf
: "CAST($idf AS text)"
);
function convertSearch($idf, array $where, array $field) {
$textTypes = "char|text";
if (strpos($where["op"], "LIKE") === false) {
$textTypes .= "|date|time(stamp)?|boolean|uuid|inet|cidr|macaddr|" . number_type();
}
return (preg_match("~$textTypes~", $field["type"]) ? $idf : "CAST($idf AS text)");
}
function quoteBinary($s) {
@@ -274,7 +291,9 @@ if (isset($_GET["pgsql"])) {
}
function get_databases() {
return get_vals("SELECT datname FROM pg_database WHERE has_database_privilege(datname, 'CONNECT') ORDER BY datname");
return get_vals("SELECT d.datname FROM pg_database d JOIN pg_roles r ON d.datdba = r.oid
WHERE d.datallowconn = TRUE AND has_database_privilege(d.datname, 'CONNECT') AND pg_has_role(r.rolname, 'USAGE')
ORDER BY d.datname");
}
function limit($query, $where, $limit, $offset = 0, $separator = " ") {
@@ -322,7 +341,7 @@ ORDER BY 1";
function table_status($name = "") {
$return = array();
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
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_table_size(c.oid) AS \"Data_length\", pg_indexes_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', 'p')
@@ -376,7 +395,7 @@ ORDER BY a.attnum"
}
$row["null"] = !$row["attnotnull"];
$row["auto_increment"] = $row['attidentity'] || preg_match('~^nextval\(~i', $row["default"]);
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1);
$row["privileges"] = array("insert" => 1, "select" => 1, "update" => 1, "where" => 1, "order" => 1);
if (preg_match('~(.+)::[^,)]+(.*)~', $row["default"], $match)) {
$row["default"] = ($match[1] == "NULL" ? null : idf_unescape($match[1]) . $match[2]);
}
@@ -773,7 +792,7 @@ AND typelem = 0"
$return = "CREATE TABLE " . idf_escape($status['nspname']) . "." . idf_escape($status['Name']) . " (\n ";
// fields' definitions
foreach ($fields as $field_name => $field) {
foreach ($fields as $field) {
$part = idf_escape($field['field']) . ' ' . $field['full_type']
. default_value($field)
. ($field['attnotnull'] ? " NOT NULL" : "");
@@ -782,10 +801,11 @@ AND typelem = 0"
// sequences for fields
if (preg_match('~nextval\(\'([^\']+)\'\)~', $field['default'], $matches)) {
$sequence_name = $matches[1];
$sq = reset(get_rows(min_version(10)
? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q($sequence_name)
$rows = get_rows(min_version(10)
? "SELECT *, cache_size AS cache_value FROM pg_sequences WHERE schemaname = current_schema() AND sequencename = " . q(idf_unescape($sequence_name))
: "SELECT * FROM $sequence_name"
));
);
$sq = reset($rows);
$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]" . ($auto_increment && $sq['last_value'] ? " START $sq[last_value]" : "") . " CACHE $sq[cache_value];";
}
@@ -858,6 +878,23 @@ AND typelem = 0"
return get_key_vals("SHOW ALL");
}
/**
* @return bool
*/
function is_strict_mode() {
return false;
}
function is_c_style_escapes() {
static $c_style = null;
if ($c_style === null) {
$c_style = get_vals("SHOW standard_conforming_strings")[0] == "off";
}
return $c_style;
}
function process_list() {
return get_rows("SELECT * FROM pg_stat_activity ORDER BY " . (min_version(9.2) ? "pid" : "procpid"));
}
@@ -897,7 +934,7 @@ AND typelem = 0"
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('Network') => array("cidr" => 43, "inet" => 43, "macaddr" => 17, "macaddr8" => 23, "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;
@@ -922,6 +959,7 @@ AND typelem = 0"
"char|text" => "||",
)
),
'c_style_escapes' => true,
);
}
}

View File

@@ -764,6 +764,17 @@ if (isset($_GET["sqlite"]) || isset($_GET["sqlite2"])) {
return $return;
}
/**
* @return bool
*/
function is_strict_mode() {
return false;
}
function is_c_style_escapes() {
return true;
}
function show_status() {
$return = array();
foreach (get_vals("PRAGMA compile_options") as $option) {

View File

@@ -537,50 +537,60 @@ class Adminer {
* @return array expressions to join by AND
*/
function selectSearchProcess($fields, $indexes) {
global $connection, $driver;
$return = array();
global $driver;
$return = [];
foreach ($indexes as $i => $index) {
if ($index["type"] == "FULLTEXT" && $_GET["fulltext"][$i] != "") {
$return[] = "MATCH (" . implode(", ", array_map('idf_escape', $index["columns"])) . ") AGAINST (" . q($_GET["fulltext"][$i]) . (isset($_GET["boolean"][$i]) ? " IN BOOLEAN MODE" : "") . ")";
}
}
foreach ((array) $_GET["where"] as $key => $val) {
if ("$val[col]$val[val]" != "" && in_array($val["op"], $this->operators)) {
foreach ((array) $_GET["where"] as $where) {
$col = $where["col"];
$op = $where["op"];
$val = $where["val"];
if ("$col$val" != "" && in_array($op, $this->operators)) {
$prefix = "";
$cond = " $val[op]";
if (preg_match('~IN$~', $val["op"])) {
$in = process_length($val["val"]);
$cond = " $op";
if (preg_match('~IN$~', $op)) {
$in = process_length($val);
$cond .= " " . ($in != "" ? $in : "(NULL)");
} elseif ($val["op"] == "SQL") {
$cond = " $val[val]"; // SQL injection
} elseif ($val["op"] == "LIKE %%") {
$cond = " LIKE " . $this->processInput($fields[$val["col"]], "%$val[val]%");
} elseif ($val["op"] == "ILIKE %%") {
$cond = " ILIKE " . $this->processInput($fields[$val["col"]], "%$val[val]%");
} elseif ($val["op"] == "FIND_IN_SET") {
$prefix = "$val[op](" . q($val["val"]) . ", ";
} elseif ($op == "SQL") {
$cond = " $val"; // SQL injection
} elseif ($op == "LIKE %%") {
$cond = " LIKE " . $this->processInput($fields[$col], "%$val%");
} elseif ($op == "ILIKE %%") {
$cond = " ILIKE " . $this->processInput($fields[$col], "%$val%");
} elseif ($op == "FIND_IN_SET") {
$prefix = "$op(" . q($val) . ", ";
$cond = ")";
} elseif (!preg_match('~NULL$~', $val["op"])) {
$cond .= " " . $this->processInput($fields[$val["col"]], $val["val"]);
} elseif (!preg_match('~NULL$~', $op)) {
$cond .= " " . $this->processInput($fields[$col], $val);
}
if ($val["col"] != "") {
$return[] = $prefix . $driver->convertSearch(idf_escape($val["col"]), $val, $fields[$val["col"]]) . $cond;
if ($col != "") {
$return[] = $prefix . $driver->convertSearch(idf_escape($col), $where, $fields[$col]) . $cond;
} else {
// find anywhere
$cols = array();
foreach ($fields as $name => $field) {
if (isset($field["privileges"]["where"])
&& (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"]))
&& (preg_match('~^[-\d.' . (preg_match('~IN$~', $op) ? ',' : '') . ']+$~', $val) || !preg_match('~' . number_type() . '|bit~', $field["type"]))
&& (!preg_match("~[\x80-\xFF]~", $val) || preg_match('~char|text|enum|set~', $field["type"]))
&& (!preg_match('~date|timestamp~', $field["type"]) || preg_match('~^\d+-\d+-\d+~', $val))
) {
$cols[] = $prefix . $driver->convertSearch(idf_escape($name), $val, $field) . $cond;
$cols[] = $prefix . $driver->convertSearch(idf_escape($name), $where, $field) . $cond;
}
}
$return[] = ($cols ? "(" . implode(" OR ", $cols) . ")" : "1 = 0");
}
}
}
return $return;
}

View File

@@ -1,11 +1,12 @@
<?php
function adminer_errors($errno, $errstr) {
return !!preg_match('~^(Trying to access array offset on value of type null|Undefined array key)~', $errstr);
return (bool)preg_match('~^(Trying to access array offset on( value of type)? null|Undefined array key)~', $errstr);
}
error_reporting(6135); // errors and warnings
set_error_handler('adminer_errors', E_WARNING);
include "../adminer/include/debug.inc.php";
include "../adminer/include/coverage.inc.php";
// disable filter.default

View File

@@ -0,0 +1,14 @@
<?php
function dump($value)
{
echo "<pre>";
var_export($value);
echo "</pre>\n";
}
function dumpe($value)
{
dump($value);
exit;
}

View File

@@ -11,6 +11,15 @@ function add_driver($id, $name) {
$drivers[$id] = $name;
}
/** Get driver name
* @param string
* @return string
*/
function get_driver($id) {
global $drivers;
return $drivers[$id];
}
/*abstract*/ class Min_SQL {
var $_conn;
@@ -138,7 +147,7 @@ function add_driver($id, $name) {
* @param array
* @return string
*/
function convertSearch($idf, $val, $field) {
function convertSearch($idf, array $where, array $field) {
return $idf;
}

View File

@@ -21,11 +21,11 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
echo "<table cellspacing='0' class='nowrap'>\n";
echo "<thead><tr>";
for ($j=0; $j < count($row); $j++) {
$field = $result->fetch_field();
$name = $field->name;
$orgtable = $field->orgtable;
$orgname = $field->orgname;
$return[$field->table] = $orgtable;
$field = (array)$result->fetch_field();
$name = $field["name"];
$orgtable = $field["orgtable"];
$orgname = $field["orgname"];
$return[$field["table"]] = $orgtable;
if ($orgtables && $jush == "sql") { // MySQL EXPLAIN
$links[$j] = ($name == "table" ? "table=" : ($name == "possible_keys" ? "indexes=" : null));
} elseif ($orgtable != "") {
@@ -46,11 +46,11 @@ function select($result, $connection2 = null, $orgtables = array(), $limit = 0)
$links[$j] = $orgtable;
}
}
if ($field->charsetnr == 63) { // 63 - binary
if ($field["charsetnr"] == 63) { // 63 - binary
$blobs[$j] = true;
}
$types[$j] = $field->type;
echo "<th" . ($orgtable != "" || $field->name != $orgname ? " title='" . h(($orgtable != "" ? "$orgtable." : "") . $orgname) . "'" : "") . ">" . h($name)
$types[$j] = $field["type"];
echo "<th" . ($orgtable != "" || $field["name"] != $orgname ? " title='" . h(($orgtable != "" ? "$orgtable." : "") . $orgname) . "'" : "") . ">" . h($name)
. ($orgtables ? doc_link(array(
'sql' => "explain-output.html#explain_" . strtolower($name),
'mariadb' => "explain/#the-columns-in-explain-select",
@@ -221,12 +221,17 @@ function process_type($field, $collate = "COLLATE") {
* @return array array("field", "type", "NULL", "DEFAULT", "ON UPDATE", "COMMENT", "AUTO_INCREMENT")
*/
function process_field($field, $type_field) {
// MariaDB exports CURRENT_TIMESTAMP as a function.
if ($field["on_update"]) {
$field["on_update"] = str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", $field["on_update"]);
}
return array(
idf_escape(trim($field["field"])),
process_type($type_field),
($field["null"] ? " NULL" : " NOT NULL"), // NULL for timestamp
default_value($field),
(preg_match('~timestamp|datetime~', $field["type"]) && $field["on_update"] ? " ON UPDATE $field[on_update]" : ""),
(preg_match('~timestamp|datetime~', $field["type"]) && $field["on_update"] ? " ON UPDATE " . $field["on_update"] : ""),
(support("comment") && $field["comment"] != "" ? " COMMENT " . q($field["comment"]) : ""),
($field["auto_increment"] ? auto_increment() : null),
);
@@ -238,7 +243,18 @@ function process_field($field, $type_field) {
*/
function default_value($field) {
$default = $field["default"];
return ($default === null ? "" : " DEFAULT " . (preg_match('~char|binary|text|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default) ? q($default) : $default));
if ($default === null) return "";
if (stripos($default, "GENERATED ") === 0) {
return " $default";
}
// MariaDB exports CURRENT_TIMESTAMP as a function.
$default = str_ireplace("current_timestamp()", "CURRENT_TIMESTAMP", $default);
$quote = preg_match('~char|binary|text|enum|set~', $field["type"]) || preg_match('~^(?![a-z])~i', $default);
return " DEFAULT " . ($quote ? q($default) : $default);
}
/** Get type class to use in CSS
@@ -279,11 +295,11 @@ function edit_fields($fields, $collations, $type = "TABLE", $foreign_keys = arra
<td><?php echo lang('Options'); /* no label required, options have their own label */ ?>
<?php if ($type == "TABLE") { ?>
<td id="label-null">NULL
<td><input type="radio" name="auto_increment_col" value=""><acronym id="label-ai" title="<?php echo lang('Auto Increment'); ?>">AI</acronym><?php echo doc_link(array(
<td><input type="radio" name="auto_increment_col" value=""><abbr id="label-ai" title="<?php echo lang('Auto Increment'); ?>">AI</abbr><?php echo doc_link(array(
'sql' => "example-auto-increment.html",
'mariadb' => "auto_increment/",
'sqlite' => "autoinc.html",
'pgsql' => "datatype.html#DATATYPE-SERIAL",
'pgsql' => "datatype-numeric.html#DATATYPE-SERIAL",
'mssql' => "ms186775.aspx",
)); ?>
<td id="label-default"<?php echo $default_class; ?>><?php echo lang('Default value'); ?>
@@ -368,25 +384,43 @@ function normalize_enum($match) {
return "'" . str_replace("'", "''", addcslashes(stripcslashes(str_replace($match[0][0] . $match[0][0], $match[0][0], substr($match[0], 1, -1))), '\\')) . "'";
}
/** Issue grant or revoke commands
* @param string GRANT or REVOKE
* @param array
* @param string
* @param string
* @return bool
*/
function grant($grant, $privileges, $columns, $on) {
if (!$privileges) {
return true;
/**
* Issue grant or revoke commands.
*
* @param bool $grant
* @param array $privileges
* @param string $columns
* @param string $on
* @param string $user
*
* @return bool
*/
function grant($grant, array $privileges, $columns, $on, $user) {
if (!$privileges) return true;
if ($privileges == ["ALL PRIVILEGES", "GRANT OPTION"]) {
if ($grant) {
return (bool) queries("GRANT ALL PRIVILEGES ON $on TO $user WITH GRANT OPTION");
} else {
return queries("REVOKE ALL PRIVILEGES ON $on FROM $user") &&
queries("REVOKE GRANT OPTION ON $on FROM $user");
}
if ($privileges == array("ALL PRIVILEGES", "GRANT OPTION")) {
// can't be granted or revoked together
return ($grant == "GRANT"
? queries("$grant ALL PRIVILEGES$on WITH GRANT OPTION")
: queries("$grant ALL PRIVILEGES$on") && queries("$grant GRANT OPTION$on")
}
if ($privileges == ["GRANT OPTION", "PROXY"]) {
if ($grant) {
return (bool) queries("GRANT PROXY ON $on TO $user WITH GRANT OPTION");
} else {
return (bool) queries("REVOKE PROXY ON $on FROM $user");
}
}
return (bool) queries(
($grant ? "GRANT " : "REVOKE ") .
preg_replace('~(GRANT OPTION)\([^)]*\)~', '$1', implode("$columns, ", $privileges) . $columns) .
" ON $on " .
($grant ? "TO " : "FROM ") . $user
);
}
return queries("$grant " . preg_replace('~(GRANT OPTION)\([^)]*\)~', '\1', implode("$columns, ", $privileges) . $columns) . $on);
}
/** Drop old object and create a new one
@@ -515,9 +549,9 @@ function tar_file($filename, $tmp_file) {
function ini_bytes($ini) {
$val = ini_get($ini);
switch (strtolower(substr($val, -1))) {
case 'g': $val *= 1024; // no break
case 'm': $val *= 1024; // no break
case 'k': $val *= 1024;
case 'g': $val = (int)$val * 1024; // no break
case 'm': $val = (int)$val * 1024; // no break
case 'k': $val = (int)$val * 1024;
}
return $val;
}

View File

@@ -477,24 +477,36 @@ function escape_key($key) {
*/
function where($where, $fields = array()) {
global $connection, $jush;
$return = array();
$conditions = [];
foreach ((array) $where["where"] as $key => $val) {
$key = bracket_escape($key, 1); // 1 - back
$column = escape_key($key);
$return[] = $column
. ($jush == "sql" && is_numeric($val) && preg_match('~\.~', $val) ? " LIKE " . q($val) // LIKE because of floats but slow with ints
: ($jush == "mssql" ? " LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val)) // LIKE because of text
: " = " . unconvert_field($fields[$key], q($val))
))
; //! enum and set
if ($jush == "sql" && preg_match('~char|text~', $fields[$key]["type"]) && preg_match("~[^ -@]~", $val)) { // not just [a-z] to catch non-ASCII characters
$return[] = "$column = " . q($val) . " COLLATE " . charset($connection) . "_bin";
if ($jush == "sql" && $fields[$key]["type"] == "json") {
$conditions[] = "$column = CAST(" . q($val) . " AS JSON)";
} elseif ($jush == "sql" && is_numeric($val) && strpos($val, ".") !== false) {
// LIKE because of floats but slow with ints.
$conditions[] = "$column LIKE " . q($val);
} elseif ($jush == "mssql") {
// LIKE because of text.
$conditions[] = "$column LIKE " . q(preg_replace('~[_%[]~', '[\0]', $val));
} else {
$conditions[] = "$column = " . unconvert_field($fields[$key], q($val));
}
// Not just [a-z] to catch non-ASCII characters.
if ($jush == "sql" && preg_match('~char|text~', $fields[$key]["type"]) && preg_match("~[^ -@]~", $val)) {
$conditions[] = "$column = " . q($val) . " COLLATE " . charset($connection) . "_bin";
}
}
foreach ((array) $where["null"] as $key) {
$return[] = escape_key($key) . " IS NULL";
$conditions[] = escape_key($key) . " IS NULL";
}
return implode(" AND ", $return);
return implode(" AND ", $conditions);
}
/** Create SQL condition from query string
@@ -914,13 +926,16 @@ function column_foreign_keys($table) {
*/
function enum_input($type, $attrs, $field, $value, $empty = null) {
global $adminer;
$return = ($empty !== null && !is_strict_mode() ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === 0) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
$return = ($empty !== null ? "<label><input type='$type'$attrs value='$empty'" . ((is_array($value) ? in_array($empty, $value) : $value === 0) ? " checked" : "") . "><i>" . lang('empty') . "</i></label>" : "");
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));
$checked = (is_int($value) ? $value == $i+1 : (is_array($value) ? in_array($i+1, $value) : $value === $val));
$return .= " <label><input type='$type'$attrs value='" . ($i+1) . "'" . ($checked ? ' checked' : '') . '>' . h($adminer->editVal($val, $field)) . '</label>';
}
return $return;
}
@@ -932,8 +947,9 @@ function enum_input($type, $attrs, $field, $value, $empty = null) {
*/
function input($field, $value, $function) {
global $types, $adminer, $jush;
$name = h(bracket_escape($field["field"]));
echo "<td class='function'>";
if (is_array($value) && !$function) {
$args = array($value);
if (version_compare(PHP_VERSION, 5.4) >= 0) {
@@ -947,13 +963,18 @@ function input($field, $value, $function) {
$function = null;
}
$functions = (isset($_GET["select"]) || $reset ? array("orig" => lang('original')) : array()) + $adminer->editFunctions($field);
$attrs = " name='fields[$name]'";
$disabled = stripos($field["default"], "GENERATED ALWAYS AS ") === 0 ? " disabled=''" : "";
$attrs = " name='fields[$name]' $disabled";
echo "<td class='function'>";
if ($field["type"] == "enum") {
echo h($functions[""]) . "<td>" . $adminer->editInput($_GET["edit"], $field, $attrs, $value);
} else {
$has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1
? "<select name='function[$name]'>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
? "<select name='function[$name]' $disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
. on_help("getTarget(event).value.replace(/^SQL\$/, '')", 1)
. script("qsl('select').onchange = functionChange;", "")
: h(reset($functions))
@@ -978,7 +999,7 @@ function input($field, $value, $function) {
$attrs .= " cols='50' rows='12'";
} else {
$rows = min(12, substr_count($value, "\n") + 1);
$attrs .= " cols='30' rows='$rows'" . ($rows == 1 ? " style='height: 1.2em;'" : ""); // 1.2em - line-height
$attrs .= " cols='30' rows='$rows'";
}
echo "<textarea$attrs>" . h($value) . '</textarea>';
} elseif ($function == "json" || preg_match('~^jsonb?$~', $field["type"])) {
@@ -1018,6 +1039,11 @@ function input($field, $value, $function) {
*/
function process_input($field) {
global $adminer, $driver;
if (stripos($field["default"], "GENERATED ALWAYS AS ") === 0) {
return null;
}
$idf = bracket_escape($field["field"]);
$function = $_POST["function"][$idf];
$value = $_POST["fields"][$idf];
@@ -1108,6 +1134,27 @@ function search_tables() {
echo ($sep ? "<p class='message'>" . lang('No tables.') : "</ul>") . "\n";
}
/**
* @param string $table
* @return array
*/
function get_partitions_info($table) {
global $connection;
$from = "FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = " . q(DB) . " AND TABLE_NAME = " . q($table);
$result = $connection->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $from ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");
$info = [];
list($info["partition_by"], $info["partition"], $info["partitions"]) = $result->fetch_row();
$partitions = get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $from AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");
$info["partition_names"] = array_keys($partitions);
$info["partition_values"] = array_values($partitions);
return $info;
}
/** Send headers for export
* @param string
* @param bool
@@ -1438,6 +1485,7 @@ function edit_form($table, $fields, $row, $update) {
$adminer->editRowPrint($table, $fields, $row, $update);
if ($row === false) {
echo "<p class='error'>" . lang('No rows.') . "\n";
return;
}
?>
<form action="" method="post" enctype="multipart/form-data" id="form">

View File

@@ -1,2 +1,2 @@
<?php
$VERSION = "4.9";
$VERSION = "4.9.3";

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Да',
'No' => 'Не',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Da',
'No' => 'Ne',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -346,4 +346,13 @@ $translations = array(
'Type has been dropped.' => 'Typ byl odstraněn.',
'Type has been created.' => 'Typ byl vytvořen.',
'Alter type' => 'Pozměnit typ',
// Plugins
'Columns' => 'Sloupce',
'Nullable' => 'Povoleno null',
'Default' => 'Výchozí',
'Yes' => 'Ano',
'No' => 'Ne',
'One Time Password' => 'Jednorázové heslo',
'Invalid OTP code.' => 'Neplatný kód OTP.',
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ja',
'No' => 'Nej',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ja',
'No' => 'Nein',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => 'Tuntematon virhe.',
'Database does not support password.' => 'Tietokanta ei tue salasanaa.',
'Disable %s or enable %s or %s extensions.' => 'Poista käytöstä %s tai ota käyttöön laajennus %s tai %s.',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Kyllä',
'No' => 'Ei',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => 'Erreur inconnue',
'Database does not support password.' => 'La base de données ne support pas les mots de passe',
'Disable %s or enable %s or %s extensions.' => 'Désactiver %s ou activer %s or %s extensions.',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Oui',
'No' => 'Non',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Si',
'No' => 'Non',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => 'Disabilita %s o abilita %s oppure %s estensioni.',
'yes' => 'si',
'no' => 'no',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Si',
'No' => 'No',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => 'გათიშეთ %s ან ჩართეთ %s ან %s გაფართოება.',
'overwrite' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -285,10 +285,10 @@ $translations = array(
'Size' => 'Izmērs',
'Compute' => 'Izskaitļot',
'You are offline.' => 'Jūs est bezsasaistē.',
'You have no privileges to update this table.' => 'jums nav pieejas labot šo tabulu.',
'You have no privileges to update this table.' => 'Jums nav pieejas labot šo tabulu.',
'Saving' => 'Saglabāšana',
'yes' => 'Jā',
'no' => 'Nē',
'yes' => 'jā',
'no' => 'nē',
'Drop %s?' => 'Dzēst %s?',
'overwrite' => 'pārrakstīt',
'DB' => 'DB',
@@ -302,4 +302,12 @@ $translations = array(
'Database does not support password.' => 'Datubāze neatbalsta paroli.',
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Jā',
'No' => 'Nē',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ya',
'No' => 'Tidak',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => 'Schakel %s uit or schakel extensies %s of %s in.',
'yes' => 'ja',
'no' => 'neen',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ja',
'No' => 'Neen',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ja',
'No' => 'Nei',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Tak',
'No' => 'Nie',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'yes' => null,
'no' => null,
'HH:MM:SS' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'yes' => null,
'no' => null,
'HH:MM:SS' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -287,8 +287,8 @@ $translations = array(
'You are offline.' => 'Вы не выполнили вход.',
'You have no privileges to update this table.' => 'У вас нет прав на обновление этой таблицы.',
'Saving' => 'Сохранение',
'yes' => 'Да',
'no' => 'Нет',
'yes' => 'да',
'no' => 'нет',
'Drop %s?' => 'Удалить %s?',
'overwrite' => 'перезаписать',
'DB' => 'DB',
@@ -302,4 +302,12 @@ $translations = array(
'Database does not support password.' => 'База данных не поддерживает пароль.',
'Disable %s or enable %s or %s extensions.' => 'Отключите %s или включите расширения %s или %s.',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Да',
'No' => 'Нет',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -265,7 +265,6 @@ $translations = array(
'Permanent link' => 'Permanentný odkaz',
'Edit all' => 'Upraviť všetko',
'HH:MM:SS' => 'HH:MM:SS',
'Drop %s?' => 'Odstrániť %s?',
'Tables have been optimized.' => 'Tabuľky boli optimalizované.',
'Materialized view' => 'Materializovaný pohľad',
@@ -302,4 +301,11 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => 'Zakážte %s alebo povoľte rozšírenie %s alebo %s.',
'yes' => 'áno',
'no' => 'nie',
'Yes' => 'Áno',
'No' => 'Nie',
'Columns' => 'Stĺpce',
'Nullable' => 'Povolené null',
'Default' => 'Predvolené',
'One Time Password' => 'Jednorázové heslo',
'Invalid OTP code.' => 'Neplatný kód OTP.',
);

View File

@@ -344,4 +344,12 @@ $translations = array(
'yes' => null,
'no' => null,
'HH:MM:SS' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Да',
'No' => 'Не',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -346,4 +346,12 @@ $translations = array(
'Type has been dropped.' => 'Typ har, typ, tagits bort.',
'Type has been created.' => 'Typ har skapats.',
'Alter type' => 'Ändra typ',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Ja',
'No' => 'Nej',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -302,4 +302,12 @@ $translations = array(
'Disable %s or enable %s or %s extensions.' => null,
'yes' => null,
'no' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Evet',
'No' => 'Hayır',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -347,4 +347,12 @@ $translations = array(
'Vacuum' => null,
'%d / ' => array(),
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Так',
'No' => 'Ні',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -346,4 +346,12 @@ $translations = array(
'Unknown error.' => null,
'Database does not support password.' => null,
'Disable %s or enable %s or %s extensions.' => null,
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => 'Có',
'No' => 'Không',
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -346,4 +346,13 @@ $translations = array(
'Type has been dropped.' => 'Xx.',
'Type has been created.' => 'Xx.',
'Alter type' => 'Xx',
// Plugins
'Columns' => 'Xx',
'Nullable' => 'Xx',
'Default' => 'Xx',
'Yes' => 'Xx',
'No' => 'Xx',
'One Time Password' => 'Xx',
'Invalid OTP code.' => 'Xx.',
);

View File

@@ -346,4 +346,13 @@ $translations = array(
'Type has been dropped.' => '已刪除類型。',
'Type has been created.' => '已建立類型。',
'Alter type' => '修改類型',
// Plugins
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -346,4 +346,12 @@ $translations = array(
'Type has been dropped.' => '已删除类型。',
'Type has been created.' => '已创建类型。',
'Alter type' => '修改类型',
'Columns' => null,
'Nullable' => null,
'Default' => null,
'Yes' => null,
'No' => null,
'One Time Password' => null,
'Invalid OTP code.' => null,
);

View File

@@ -32,8 +32,8 @@ if (!$error && $_POST) {
}
if (is_string($query)) { // get_file() returns error as number, fread() as false
if (function_exists('memory_get_usage')) {
@ini_set("memory_limit", max(ini_bytes("memory_limit"), 2 * strlen($query) + memory_get_usage() + 8e6)); // @ - may be disabled, 2 - substr and trim, 8e6 - other variables
if (function_exists('memory_get_usage') && ($memory_limit = ini_bytes("memory_limit")) != "-1") {
@ini_set("memory_limit", max($memory_limit, 2 * strlen($query) + memory_get_usage() + 8e6)); // @ - may be disabled, 2 - substr and trim, 8e6 - other variables
}
if ($query != "" && strlen($query) < 1e6) { // don't add big queries
@@ -81,7 +81,21 @@ if (!$error && $_POST) {
$offset = $pos + strlen($found);
if ($found && rtrim($found) != $delimiter) { // find matching quote or comment end
while (preg_match('(' . ($found == '/*' ? '\*/' : ($found == '[' ? ']' : (preg_match('~^-- |^#~', $found) ? "\n" : preg_quote($found) . "|\\\\."))) . '|$)s', $query, $match, PREG_OFFSET_CAPTURE, $offset)) { //! respect sql_mode NO_BACKSLASH_ESCAPES
$c_style_escapes = is_c_style_escapes() || ($jush == "pgsql" && ($pos > 0 && strtolower($query[$pos - 1]) == "e"));
$pattern = '(';
if ($found == '/*') {
$pattern .= '\*/';
} elseif ($found == '[') {
$pattern .= ']';
} elseif (preg_match('~^-- |^#~', $found)) {
$pattern .= "\n";
} else {
$pattern .= preg_quote($found) . ($c_style_escapes ? "|\\\\." : "");
}
$pattern .= '|$)s';
while (preg_match($pattern, $query, $match, PREG_OFFSET_CAPTURE, $offset)) {
$s = $match[0][0];
if (!$s && $fp && !feof($fp)) {
$query .= fread($fp, 1e5);
@@ -123,7 +137,7 @@ if (!$error && $_POST) {
if ($connection->error) {
echo ($_POST["only_errors"] ? $print : "");
echo "<p class='error'>" . lang('Error in query') . ($connection->errno ? " ($connection->errno)" : "") . ": " . error() . "\n";
echo "<p class='error'>" . lang('Error in query') . (!empty($connection->errno) ? " ($connection->errno)" : "") . ": " . error() . "\n";
$errors[] = " <a href='#sql-$commands'>$commands</a>";
if ($_POST["error_stops"]) {
break 2;
@@ -169,7 +183,8 @@ if (!$error && $_POST) {
stop_session();
}
if (!$_POST["only_errors"]) {
echo "<p class='message' title='" . h($connection->info) . "'>" . lang('Query executed OK, %d row(s) affected.', $affected) . "$time\n";
$title = isset($connection->info) ? "title='" . h($connection->info) . "'" : "";
echo "<p class='message' $title>" . lang('Query executed OK, %d row(s) affected.', $affected) . "$time\n";
}
}
echo ($warnings ? "<div id='$warnings_id' class='hidden'>\n$warnings</div>\n" : "");

View File

@@ -19,15 +19,18 @@ fieldset { display: inline; vertical-align: top; padding: .5em .8em; margin: .8e
p { margin: .8em 20px 0 0; }
img { vertical-align: middle; border: 0; }
td img { max-width: 200px; max-height: 200px; }
code { background: #eee; }
tbody tr:hover td, tbody tr:hover th { background: #eee; }
code { font-size: 110%; padding: 1px 2px; background: #eee; }
pre { margin: 1em 0 0; }
pre, textarea { font: 100%/1.25 monospace; }
pre code { display: block; font-size: 100%; }
pre, textarea { font: 110%/1.25 monospace; }
pre.jush { background: #fff; }
input, select { vertical-align: middle; }
input.default { box-shadow: 1px 1px 1px #777; }
input.required { box-shadow: 1px 1px 1px red; }
input.maxlength { box-shadow: 1px 1px 1px red; }
input.wayoff { left: -1000px; position: absolute; }
.center { text-align: center; }
.block { display: block; }
.version { color: #777; font-size: 67%; }
.js .hidden, .nojs .jsonly { display: none; }
@@ -69,7 +72,7 @@ input.wayoff { left: -1000px; position: absolute; }
#menu p, #logins, #tables { padding: .8em 1em; margin: 0; border-bottom: 1px solid #ccc; }
#logins li, #tables li { list-style: none; }
#dbs { overflow: hidden; }
#logins, #tables { white-space: nowrap; overflow: auto; }
#logins, #tables { white-space: nowrap; overflow: hidden; }
#logins a, #tables a, #tables span { background: #fff; }
#content { margin: 2em 0 0 21em; padding: 10px 20px 20px 0; }
#lang { position: absolute; top: 0; left: 0; line-height: 1.8em; padding: .3em 1em; }

View File

@@ -54,7 +54,7 @@ function bodyLoad(version, maria) {
function formField(form, name) {
// required in IE < 8, form.elements[name] doesn't work
for (var i=0; i < form.length; i++) {
if (form[i].name == name) {
if (form[i].name === name) {
return form[i];
}
}
@@ -104,7 +104,7 @@ var dbPrevious = {};
*/
function dbMouseDown(event) {
dbCtrl = isCtrl(event);
if (dbPrevious[this.name] == undefined) {
if (dbPrevious[this.name] === undefined) {
dbPrevious[this.name] = this.value;
}
}
@@ -118,7 +118,7 @@ function dbChange() {
}
this.form.submit();
this.form.target = '';
if (dbCtrl && dbPrevious[this.name] != undefined) {
if (dbCtrl && dbPrevious[this.name] !== undefined) {
this.value = dbPrevious[this.name];
dbPrevious[this.name] = undefined;
}
@@ -145,23 +145,23 @@ function selectFieldChange() {
for (var i=0; i < selects.length; i++) {
var select = selects[i];
var col = selectValue(select);
var match = /^(where.+)col\]/.exec(select.name);
var match = /^(where.+)col]/.exec(select.name);
if (match) {
var op = selectValue(form[match[1] + 'op]']);
var val = form[match[1] + 'val]'].value;
if (col in indexColumns && (!/LIKE|REGEXP/.test(op) || (op == 'LIKE' && val.charAt(0) != '%'))) {
if (col in indexColumns && (!/LIKE|REGEXP/.test(op) || (op === 'LIKE' && val.charAt(0) !== '%'))) {
return true;
} else if (col || val) {
ok = false;
}
}
if ((match = /^(columns.+)fun\]/.exec(select.name))) {
if ((match = /^(columns.+)fun]/.exec(select.name))) {
if (/^(avg|count|count distinct|group_concat|max|min|sum)$/.test(col)) {
group = true;
}
var val = selectValue(form[match[1] + 'col]']);
if (val) {
columns[col && col != 'count' ? '' : val] = 1;
columns[col && col !== 'count' ? '' : val] = 1;
}
}
if (col && /^order/.test(select.name)) {
@@ -194,7 +194,7 @@ var added = '.', rowCount;
* @return boolean
*/
function delimiterEqual(val, a, b) {
return (val == a + '_' + b || val == a + b || val == a + b.charAt(0).toUpperCase() + b.substr(1));
return (val === a + '_' + b || val === a + b || val === a + b.charAt(0).toUpperCase() + b.substr(1));
}
/** Escape string to use as identifier
@@ -255,7 +255,7 @@ function editingClick(event) {
} else if (/^drop_col\[/.test(name)) {
editingRemoveRow.call(el, 'fields\$1[field]');
} else {
if (name == 'auto_increment_col') {
if (name === 'auto_increment_col') {
var field = el.form['fields[' + el.value + '][field]'];
if (!field.value) {
field.value = 'id';
@@ -273,7 +273,7 @@ function editingClick(event) {
*/
function editingInput(event) {
var el = getTarget(event);
if (/\[default\]$/.test(el.name)) {
if (/\[default]$/.test(el.name)) {
el.previousSibling.checked = true;
}
}
@@ -290,7 +290,7 @@ function editingNameChange() {
for (var i = opts.length; i--; ) {
var match = /(.+)`(.+)/.exec(opts[i].value);
if (!match) { // common type
if (candidate && i == opts.length - 2 && val == opts[candidate].value.replace(/.+`/, '') && name == 'fields[1]') { // single target table, link to column, first field - probably `id`
if (candidate && i === opts.length - 2 && val === opts[candidate].value.replace(/.+`/, '') && name === 'fields[1]') { // single target table, link to column, first field - probably `id`
return;
}
break;
@@ -300,7 +300,7 @@ function editingNameChange() {
var tables = [ table, table.replace(/s$/, ''), table.replace(/es$/, '') ];
for (var j=0; j < tables.length; j++) {
table = tables[j];
if (val == column || val == table || delimiterEqual(val, table, column) || delimiterEqual(val, column, table)) {
if (val === column || val === table || delimiterEqual(val, table, column) || delimiterEqual(val, column, table)) {
if (candidate) {
return;
}
@@ -335,7 +335,7 @@ function editingAddRow(focus) {
tags2 = qsa('input', row2);
var input = tags2[0]; // IE loose tags2 after insertBefore()
for (var i=0; i < tags.length; i++) {
if (tags[i].name == 'auto_increment_col') {
if (tags[i].name === 'auto_increment_col') {
tags2[i].value = x;
tags2[i].checked = false;
}
@@ -397,7 +397,7 @@ function editingTypeChange() {
var text = selectValue(type);
for (var i=0; i < type.form.elements.length; i++) {
var el = type.form.elements[i];
if (el.name == name + '[length]') {
if (el.name === name + '[length]') {
if (!(
(/(char|binary)$/.test(lastType) && /(char|binary)$/.test(text))
|| (/(enum|set)$/.test(lastType) && /(enum|set)$/.test(text))
@@ -406,19 +406,19 @@ function editingTypeChange() {
}
el.oninput.apply(el);
}
if (lastType == 'timestamp' && el.name == name + '[has_default]' && /timestamp/i.test(formField(type.form, name + '[default]').value)) {
if (lastType === 'timestamp' && el.name == name + '[has_default]' && /timestamp/i.test(formField(type.form, name + '[default]').value)) {
el.checked = false;
}
if (el.name == name + '[collation]') {
if (el.name === name + '[collation]') {
alterClass(el, 'hidden', !/(char|text|enum|set)$/.test(text));
}
if (el.name == name + '[unsigned]') {
if (el.name === name + '[unsigned]') {
alterClass(el, 'hidden', !/(^|[^o])int(?!er)|numeric|real|float|double|decimal|money/.test(text));
}
if (el.name == name + '[on_update]') {
if (el.name === name + '[on_update]') {
alterClass(el, 'hidden', !/timestamp|datetime/.test(text)); // MySQL supports datetime since 5.6.5
}
if (el.name == name + '[on_delete]') {
if (el.name === name + '[on_delete]') {
alterClass(el, 'hidden', !/`/.test(text));
}
}
@@ -457,13 +457,13 @@ function enumValues(s) {
var offset = 0;
var match;
while (match = re.exec(s)) {
if (offset != match.index) {
if (offset !== match.index) {
break;
}
result.push(match[2].replace(/'(')|\\(.)/g, '$1$2'));
offset += match[0].length;
}
return (offset == s.length ? result.join('\n') : s);
return (offset === s.length ? result.join('\n') : s);
}
/** Finish editing of enum or set
@@ -531,7 +531,7 @@ function dumpClick(event) {
var el = parentTag(getTarget(event), 'label');
if (el) {
el = qs('input', el);
var match = /(.+)\[\]$/.exec(el.name);
var match = /(.+)\[]$/.exec(el.name);
if (match) {
checkboxClick.call(el, event);
formUncheck('check-' + match[1]);
@@ -549,7 +549,7 @@ function foreignAddRow() {
this.onchange = function () { };
var selects = qsa('select', row);
for (var i=0; i < selects.length; i++) {
selects[i].name = selects[i].name.replace(/\]/, '1$&');
selects[i].name = selects[i].name.replace(']', '1]');
selects[i].selectedIndex = 0;
}
parentTag(this, 'table').appendChild(row);
@@ -585,7 +585,7 @@ function indexesChangeColumn(prefix) {
for (var tag in { 'select': 1, 'input': 1 }) {
var columns = qsa(tag, parentTag(this, 'td'));
for (var i=0; i < columns.length; i++) {
if (/\[columns\]/.test(columns[i].name)) {
if (/\[columns]/.test(columns[i].name)) {
var value = selectValue(columns[i]);
if (value) {
names.push(value);
@@ -593,7 +593,7 @@ function indexesChangeColumn(prefix) {
}
}
}
this.form[this.name.replace(/\].*/, '][name]')].value = prefix + names.join('_');
this.form[this.name.replace(/].*/, '][name]')].value = prefix + names.join('_');
}
/** Add column for index
@@ -602,9 +602,9 @@ function indexesChangeColumn(prefix) {
*/
function indexesAddColumn(prefix) {
var field = this;
var select = field.form[field.name.replace(/\].*/, '][type]')];
var select = field.form[field.name.replace(/].*/, '][type]')];
if (!select.selectedIndex) {
while (selectValue(select) != "INDEX" && select.selectedIndex < select.options.length) {
while (selectValue(select) !== "INDEX" && select.selectedIndex < select.options.length) {
select.selectedIndex++;
}
select.onchange();
@@ -613,15 +613,15 @@ function indexesAddColumn(prefix) {
var selects = qsa('select', column);
for (var i = 0; i < selects.length; i++) {
select = selects[i];
select.name = select.name.replace(/\]\[\d+/, '$&1');
select.name = select.name.replace(/]\[\d+/, '$&1');
select.selectedIndex = 0;
}
field.onchange = partial(indexesChangeColumn, prefix);
var inputs = qsa('input', column);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
input.name = input.name.replace(/\]\[\d+/, '$&1');
if (input.type != 'checkbox') {
input.name = input.name.replace(/]\[\d+/, '$&1');
if (input.type !== 'checkbox') {
input.value = '';
}
}
@@ -670,7 +670,7 @@ var that, x, y; // em and tablePos defined in schema.inc.php
* @this HTMLElement
*/
function schemaMousedown(event) {
if ((event.which ? event.which : event.button) == 1) {
if ((event.which ? event.which : event.button) === 1) {
that = this;
x = event.clientX - this.offsetLeft;
y = event.clientY - this.offsetTop;
@@ -687,12 +687,12 @@ function schemaMousemove(event) {
var divs = qsa('div', that);
var lineSet = { };
for (var i=0; i < divs.length; i++) {
if (divs[i].className == 'references') {
if (divs[i].className === 'references') {
var div2 = qs('[id="' + (/^refs/.test(divs[i].id) ? 'refd' : 'refs') + divs[i].id.substr(4) + '"]');
var ref = (tablePos[divs[i].title] ? tablePos[divs[i].title] : [ div2.parentNode.offsetTop / em, 0 ]);
var left1 = -1;
var id = divs[i].id.replace(/^ref.(.+)-.+/, '$1');
if (divs[i].parentNode != div2.parentNode) {
if (divs[i].parentNode !== div2.parentNode) {
left1 = Math.min(0, ref[1] - left) - 1;
divs[i].style.left = left1 + 'em';
divs[i].querySelector('div').style.width = -left1 + 'em';
@@ -704,7 +704,7 @@ function schemaMousemove(event) {
var line = qs('[id="' + divs[i].id.replace(/^....(.+)-.+$/, 'refl$1') + '"]');
var top1 = top + divs[i].offsetTop / em;
var top2 = top + div2.offsetTop / em;
if (divs[i].parentNode != div2.parentNode) {
if (divs[i].parentNode !== div2.parentNode) {
top2 += ref[0] - top;
line.querySelector('div').style.height = Math.abs(top1 - top2) + 'em';
}
@@ -752,7 +752,7 @@ function helpMouseover(event, text, side) {
var target = getTarget(event);
if (!text) {
helpClose();
} else if (window.jush && (!helpIgnore || this != target)) {
} else if (window.jush && (!helpIgnore || this !== target)) {
helpOpen = 1;
var help = qs('#help');
help.innerHTML = text;
@@ -771,7 +771,7 @@ function helpMouseover(event, text, side) {
*/
function helpMouseout(event) {
helpOpen = 0;
helpIgnore = (this != getTarget(event));
helpIgnore = (this !== getTarget(event));
setTimeout(function () {
if (!helpOpen) {
helpClose();

View File

@@ -4,7 +4,7 @@
* @param [HTMLElement] defaults to document
* @return HTMLElement
*/
function qs(selector, context) {
function qs(selector, context = null) {
return (context || document).querySelector(selector);
}
@@ -13,7 +13,7 @@ function qs(selector, context) {
* @param [HTMLElement] defaults to document
* @return HTMLElement
*/
function qsl(selector, context) {
function qsl(selector, context = null) {
var els = qsa(selector, context);
return els[els.length - 1];
}
@@ -23,7 +23,7 @@ function qsl(selector, context) {
* @param [HTMLElement] defaults to document
* @return NodeList
*/
function qsa(selector, context) {
function qsa(selector, context = null) {
return (context || document).querySelectorAll(selector);
}
@@ -79,7 +79,7 @@ function alterClass(el, className, enable) {
*/
function toggle(id) {
var el = qs('#' + id);
el.className = (el.className == 'hidden' ? '' : 'hidden');
el.className = (el.className === 'hidden' ? '' : 'hidden');
return false;
}
@@ -111,7 +111,7 @@ function verifyVersion(current, url, token) {
if (window.postMessage && window.addEventListener) {
iframe.style.display = 'none';
addEventListener('message', function (event) {
if (event.origin == 'https://www.adminer.org') {
if (event.origin === 'https://www.adminer.org') {
var match = /version=(.+)/.exec(event.data);
if (match) {
cookie('adminer_version=' + match[1], 1);
@@ -139,7 +139,7 @@ function selectValue(select) {
/** Verify if element has a specified tag name
* @param HTMLElement
* @param string regular expression
* @return bool
* @return boolean
*/
function isTag(el, tag) {
var re = new RegExp('^(' + tag + ')$', 'i');
@@ -181,8 +181,8 @@ function selectCount(id, count) {
var inputs = qsa('input', el.parentNode.parentNode);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.type == 'submit') {
input.disabled = (count == '0');
if (input.type === 'submit') {
input.disabled = (count === '0');
}
}
}
@@ -211,13 +211,21 @@ function tableCheck() {
}
}
/** Uncheck single element
* @param string
*/
/**
* Uncheck single element.
*/
function formUncheck(id) {
var el = qs('#' + id);
el.checked = false;
trCheck(el);
formUncheckAll("#" + id);
}
/**
* Uncheck elements matched by selector.
*/
function formUncheckAll(selector) {
for (const element of qsa(selector)) {
element.checked = false;
trCheck(element);
}
}
/** Get number of checked elements matching given name
@@ -252,7 +260,7 @@ function tableClick(event, click) {
var el = getTarget(event);
while (!isTag(el, 'tr')) {
if (isTag(el, 'table|a|input|textarea')) {
if (el.type != 'checkbox') {
if (el.type !== 'checkbox') {
return;
}
checkboxClick.call(el, event);
@@ -268,7 +276,7 @@ function tableClick(event, click) {
el.checked = !el.checked;
el.onclick && el.onclick();
}
if (el.name == 'check[]') {
if (el.name === 'check[]') {
el.form['all'].checked = false;
formUncheck('all-page');
}
@@ -288,7 +296,7 @@ function checkboxClick(event) {
if (!this.name) {
return;
}
if (event.shiftKey && (!lastChecked || lastChecked.name == this.name)) {
if (event.shiftKey && (!lastChecked || lastChecked.name === this.name)) {
var checked = (lastChecked ? lastChecked.checked : true);
var inputs = qsa('input', parentTag(this, 'table'));
var checking = !lastChecked;
@@ -333,7 +341,7 @@ function setHtml(id, html) {
*/
function nodePosition(el) {
var pos = 0;
while (el = el.previousSibling) {
while ((el = el.previousSibling)) {
pos++;
}
return pos;
@@ -345,7 +353,7 @@ function nodePosition(el) {
*/
function pageClick(href, page) {
if (!isNaN(page) && page) {
location.href = href + (page != 1 ? '&page=' + (page - 1) : '');
location.href = href + (page !== 1 ? '&page=' + (page - 1) : '');
}
}
@@ -366,7 +374,7 @@ function menuOver(event) {
* @this HTMLElement
*/
function menuOut() {
this.style.overflow = 'auto';
this.style.overflow = 'hidden';
}
@@ -388,7 +396,7 @@ function selectAddRow() {
for (var i=0; i < inputs.length; i++) {
inputs[i].name = inputs[i].name.replace(/[a-z]\[\d+/, '$&1');
inputs[i].className = '';
if (inputs[i].type == 'checkbox') {
if (inputs[i].type === 'checkbox') {
inputs[i].checked = false;
} else {
inputs[i].value = '';
@@ -402,7 +410,7 @@ function selectAddRow() {
* @this HTMLInputElement
*/
function selectSearchKeydown(event) {
if (event.keyCode == 13 || event.keyCode == 10) {
if (event.keyCode === 13 || event.keyCode === 10) {
this.onsearch = function () {
};
}
@@ -445,11 +453,11 @@ function selectSearch(name) {
for (var i=0; i < divs.length; i++) {
var div = divs[i];
var el = qs('[name$="[col]"]', div);
if (el && selectValue(el) == name) {
if (el && selectValue(el) === name) {
break;
}
}
if (i == divs.length) {
if (i === divs.length) {
div.firstChild.value = name;
div.firstChild.onchange();
}
@@ -487,7 +495,7 @@ function bodyKeydown(event, button) {
if (target.jushTextarea) {
target = target.jushTextarea;
}
if (isCtrl(event) && (event.keyCode == 13 || event.keyCode == 10) && isTag(target, 'select|textarea|input')) { // 13|10 - Enter
if (isCtrl(event) && (event.keyCode === 13 || event.keyCode === 10) && isTag(target, 'select|textarea|input')) { // 13|10 - Enter
target.blur();
if (button) {
target.form[button].click();
@@ -508,7 +516,7 @@ function bodyKeydown(event, button) {
*/
function bodyClick(event) {
var target = getTarget(event);
if ((isCtrl(event) || event.shiftKey) && target.type == 'submit' && isTag(target, 'input')) {
if ((isCtrl(event) || event.shiftKey) && target.type === 'submit' && isTag(target, 'input')) {
target.form.target = '_blank';
setTimeout(function () {
// if (isCtrl(event)) { focus(); } doesn't work
@@ -524,9 +532,9 @@ function bodyClick(event) {
* @return boolean
*/
function editingKeydown(event) {
if ((event.keyCode == 40 || event.keyCode == 38) && isCtrl(event)) { // 40 - Down, 38 - Up
if ((event.keyCode === 40 || event.keyCode === 38) && isCtrl(event)) { // 40 - Down, 38 - Up
var target = getTarget(event);
var sibling = (event.keyCode == 40 ? 'nextSibling' : 'previousSibling');
var sibling = (event.keyCode === 40 ? 'nextSibling' : 'previousSibling');
var el = target.parentNode.parentNode[sibling];
if (el && (isTag(el, 'tr') || (el = el[sibling])) && isTag(el, 'tr') && (el = el.childNodes[nodePosition(target.parentNode)]) && (el = el.childNodes[nodePosition(target)])) {
el.focus();
@@ -614,7 +622,7 @@ function ajax(url, callback, data, message) {
}
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.readyState === 4) {
if (/^2/.test(request.status)) {
callback(request);
} else {
@@ -656,7 +664,7 @@ function ajaxForm(form, message, button) {
if (/^file$/i.test(el.type) && el.value) {
return false;
}
if (!/^(checkbox|radio|submit|file)$/i.test(el.type) || el.checked || el == button) {
if (!/^(checkbox|radio|submit|file)$/i.test(el.type) || el.checked || el === button) {
data.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(isTag(el, 'select') ? selectValue(el) : el.value));
}
}
@@ -703,7 +711,7 @@ function selectClick(event, text, warning) {
if (!event) {
event = window.event;
}
if (event.keyCode == 27 && !event.shiftKey && !event.altKey && !isCtrl(event)) { // 27 - Esc
if (event.keyCode === 27 && !event.shiftKey && !event.altKey && !isCtrl(event)) { // 27 - Esc
inputBlur.apply(input);
td.innerHTML = original;
}
@@ -733,7 +741,7 @@ function selectClick(event, text, warning) {
td.appendChild(input);
setupSubmitHighlight(td);
input.focus();
if (text == 2) { // long text
if (text === 2) { // long text
return ajax(location.href + '&' + encodeURIComponent(td.id) + '=', function (request) {
if (request.responseText) {
input.value = request.responseText;
@@ -855,7 +863,7 @@ function findDefaultSubmit(el) {
var inputs = qsa('input', el.form);
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.type == 'submit' && !input.style.zIndex) {
if (input.type === 'submit' && !input.style.zIndex) {
return input;
}
}

View File

@@ -85,8 +85,8 @@ if ($_POST && !$error) {
unset($grants[$object]);
}
if (preg_match('~^(.+)\s*(\(.*\))?$~U', $object, $match) && (
!grant("REVOKE", $revoke, $match[2], " ON $match[1] FROM $new_user") //! SQL injection
|| !grant("GRANT", $grant, $match[2], " ON $match[1] TO $new_user")
!grant(false, $revoke, $match[2], $match[1], $new_user) //! SQL injection
|| !grant(true, $grant, $match[2], $match[1], $new_user)
)) {
$error = true;
break;
@@ -100,7 +100,7 @@ if ($_POST && !$error) {
} elseif (!isset($_GET["grant"])) {
foreach ($grants as $object => $revoke) {
if (preg_match('~^(.+)(\(.*\))?$~U', $object, $match)) {
grant("REVOKE", array_keys($revoke), $match[2], " ON $match[1] FROM $new_user");
grant(false, array_keys($revoke), $match[2], $match[1], $new_user);
}
}
}
@@ -150,33 +150,63 @@ foreach ($grants as $object => $grant) {
}
echo "</thead>\n";
foreach (array(
foreach ([
"" => "",
"Server Admin" => lang('Server'),
"Databases" => lang('Database'),
"Tables" => lang('Table'),
"Columns" => lang('Column'),
"Procedures" => lang('Routine'),
) as $context => $desc) {
] as $context => $desc) {
foreach ((array) $privileges[$context] as $privilege => $comment) {
echo "<tr" . odd() . "><td" . ($desc ? ">$desc<td" : " colspan='2'") . ' lang="en" title="' . h($comment) . '">' . h($privilege);
echo "<tr" . odd() . ">";
if ($desc) {
echo "<td>$desc</td>";
}
echo "<td" . (!$desc ? " colspan='2'" : "") . ' lang="en" title="' . h($comment) . '">' . h($privilege) . "</td>";
$i = 0;
foreach ($grants as $object => $grant) {
$name = "'grants[$i][" . h(strtoupper($privilege)) . "]'";
$value = $grant[strtoupper($privilege)];
if ($context == "Server Admin" && $object != (isset($grants["*.*"]) ? "*.*" : ".*")) {
echo "<td>";
$proxiedUser = strpos($object, "@") !== false;
$newObject = $object == ".*";
$allPrivileges = $privilege == "All privileges";
$grantOption = $privilege == "Grant option";
if ($object == "*.*" && $privilege == "Proxy") {
echo "<td></td>";
} elseif ($proxiedUser && $privilege != "Proxy" && !$grantOption) {
echo "<td></td>";
} elseif ($context == "Server Admin" && $object != (isset($grants["*.*"]) ? "*.*" : ".*") && !(($proxiedUser || $newObject) && $privilege == "Proxy")) {
echo "<td></td>";
} elseif (isset($_GET["grant"])) {
echo "<td><select name=$name><option><option value='1'" . ($value ? " selected" : "") . ">" . lang('Grant') . "<option value='0'" . ($value == "0" ? " selected" : "") . ">" . lang('Revoke') . "</select>";
echo "<td><select name=$name>" .
"<option></option>" .
"<option value='1'" . ($value ? " selected" : "") . ">" . lang('Grant') . "</option>" .
"<option value='0'" . ($value == "0" ? " selected" : "") . ">" . lang('Revoke') . "</option>" .
"</select></td>";
} else {
echo "<td align='center'><label class='block'>";
echo "<input type='checkbox' name=$name value='1'" . ($value ? " checked" : "") . ($privilege == "All privileges"
? " id='grants-$i-all'>" //! uncheck all except grant if all is checked
: ">" . ($privilege == "Grant option" ? "" : script("qsl('input').onclick = function () { if (this.checked) formUncheck('grants-$i-all'); };")));
echo "<td class='center'><label class='block'>";
echo "<input type='checkbox' name=$name value='1'" .
($value ? " checked" : "") .
($allPrivileges ? " id='grants-$i-all'" : (!$grantOption ? " class='grants-$i'" : "")) .
">";
if ($allPrivileges) {
echo script("qsl('input').onclick = function () { if (this.checked) formUncheckAll('.grants-$i'); };");
} elseif (!$grantOption) {
echo script("qsl('input').onclick = function () { if (this.checked) formUncheck('grants-$i-all'); };");
}
echo "</label>";
}
$i++;
}
echo "</tr>";
}
}

View File

@@ -1,3 +1,45 @@
Adminer 4.9.3 (released 2024-10-02):
- MySQL, PostgreSQL: Fix queries splitting and string constants.
- MySQL: Fix where clause for JSON column.
- MySQL: Fix editing user's proxy privilege, refactoring.
- MariaDB: Fix comparing CURRENT_TIMESTAMP definition while altering a table.
- PostgreSQL: Fix editing record that contains a field with GENERATED ALWAYS default value.
- Fix using undefined Min_DB::info property.
- Do not include unchanged PARTITION BY definition into ALTER TABLE query.
- Do not limit unlimited memory while executing queries.
- Fix number conversion warning while reading INI settings.
- Hide invalid edit form if table record is not found.
- CSS: Fix background color of <pre> used as edit field.
- CSS: Bigger font size for code blocks.
Adminer 4.9.2 (released 2024-09-18):
- Fix textarea height for single-line inputs (used typically for SQLite text field).
- Fix undefined property in error message if driver does not support error number (e.g. PostgreSQL).
- PostgreSQL: Fix search fields configuration (regression from 4.9).
- PostgreSQL: Fix search condition for network address types, add macaddr8 type.
- PostgreSQL: Fix exporting CREATE TABLE query with GENERATED default values.
- PostgreSQL: Fix exporting CREATE TABLE query with sequence default value.
- PostgreSQL: Allow to set connection's sslmode with AdminerLoginSsl plugin.
- MySQL: Do not show 'empty' enum value in strict mode.
- Editor: Fix searching in tables.
- Function to retrieve driver name that can be used in plugins.
Adminer 4.9.1 (released 2024-09-09):
- Compatibility with PHP 8.3.
- Fix compiling jush external files.
- Improved displaying of long table names in menu.
- Replace deprecated <acronym> with <abbr>.
- Add support for translations in plugins.
- Add .editorconfig file.
- MySQL: Add unix_timestamp to functions.
- PostgreSQL: Show only accessible databases.
- PostgreSQL: Make data length calculation more accurate.
- PostgreSQL: Fix documentation link for SERIAL type.
- PostgreSQL: Fix undefined properties on PHP 8.
- Elasticsearch: Fix field selection.
- AdminerEditForeign: Refactor and fix the plugin.
- AdminerLoginOtp: Autocomplete hints for OTP input field, code refactoring.
Adminer 4.9 (released 2024-08-19):
- Validate server input in login form.
- Validate connection to server in HTTP based drivers.

View File

@@ -418,7 +418,7 @@ if ($driver) {
if (count($drivers) == 1) {
$file = str_replace('<?php echo html_select("auth[driver]", $drivers, DRIVER) . "\n"; ?>', "<input type='hidden' name='auth[driver]' value='" . ($driver == "mysql" ? "server" : $driver) . "'>" . reset($drivers), $file);
}
$file = preg_replace('(;../vendor/vrana/jush/modules/jush-(?!textarea\.|txt\.|js\.|' . preg_quote($driver == "mysql" ? "sql" : $driver) . '\.)[^.]+.js)', '', $file);
$file = preg_replace('(;\.\./vendor/vrana/jush/modules/jush-(?!textarea\.|txt\.|js\.|' . preg_quote($driver == "mysql" ? "sql" : $driver) . '\.)[^.]+.js)', '', $file);
$file = preg_replace_callback('~doc_link\(array\((.*)\)\)~sU', function ($match) use ($driver) {
list(, $links) = $match;
$links = preg_replace("~'(?!(" . ($driver == "mysql" ? "sql|mariadb" : $driver) . ")')[^']*' => [^,]*,?~", '', $links);
@@ -427,8 +427,8 @@ if ($driver) {
//! strip doc_link() definition
}
if ($project == "editor") {
$file = preg_replace('~;.\.\/externals/jush/jush\.css~', '', $file);
$file = preg_replace('~compile_file\(\'\.\./(externals/jush/modules/jush\.js|adminer/static/[^.]+\.gif)[^)]+\)~', "''", $file);
$file = preg_replace('~;\.\./vendor/vrana/jush/jush\.css~', '', $file);
$file = preg_replace('~compile_file\(\'\.\./(vendor/vrana/jush/modules/jush\.js|adminer/static/[^.]+\.gif)[^)]+\)~', "''", $file);
}
$file = preg_replace_callback("~lang\\('((?:[^\\\\']+|\\\\.)*)'([,)])~s", 'lang_ids', $file);
$file = preg_replace_callback('~\b(include|require) "([^"]*\$LANG.inc.php)";~', 'put_file_lang', $file);
@@ -440,14 +440,14 @@ if ($_SESSION["lang"]) {
$file = str_replace('<?php echo $LANG; ?>', $_SESSION["lang"], $file);
}
$file = str_replace('<?php echo script_src("static/editing.js"); ?>' . "\n", "", $file);
$file = preg_replace('~\s+echo script_src\("\.\./externals/jush/modules/jush-(textarea|txt|js|\$jush)\.js"\);~', '', $file);
$file = preg_replace('~\s+echo script_src\("\.\./vendor/vrana/jush/modules/jush-(textarea|txt|js|\$jush)\.js"\);~', '', $file);
$file = str_replace('<link rel="stylesheet" type="text/css" href="../vendor/vrana/jush/jush.css">' . "\n", "", $file);
$file = preg_replace_callback("~compile_file\\('([^']+)'(?:, '([^']*)')?\\)~", 'compile_file', $file); // integrate static files
$replace = 'preg_replace("~\\\\\\\\?.*~", "", ME) . "?file=\1&version=' . substr(md5(microtime()), 0, 8) . '"';
$file = preg_replace('~\.\./adminer/static/(default\.css|favicon\.ico)~', '<?php echo h(' . $replace . '); ?>', $file);
$file = preg_replace('~"\.\./adminer/static/(functions\.js)"~', $replace, $file);
$file = preg_replace('~\.\./adminer/static/([^\'"]*)~', '" . h(' . $replace . ') . "', $file);
$file = preg_replace('~"\.\./externals/jush/modules/(jush\.js)"~', $replace, $file);
$file = preg_replace('~"\.\./vendor/vrana/jush/modules/(jush\.js)"~', $replace, $file);
$file = preg_replace("~<\\?php\\s*\\?>\n?|\\?>\n?<\\?php~", '', $file);
$file = php_shrink($file);

View File

@@ -26,7 +26,7 @@
"GPL-2.0-only"
],
"require": {
"php": "5.6 - 8.1",
"php": "5.6 - 8.3",
"ext-pdo": "*",
"ext-json": "*",
"vrana/jush": "@dev",

View File

@@ -374,7 +374,7 @@ thead td, thead th {
padding: 7px 2px;
}
thead td acronym, thead td sup, thead th acronym, thead th sup {
thead td abbr, thead td sup, thead th acronym, thead th sup {
color: #cdf
}

View File

@@ -136,7 +136,7 @@ thead th, thead td {
color:#FFFFFF;
padding-right:10px;
}
thead th acronym, thead td acronym, thead th sup, thead td sup{
thead th abbr, thead td acronym, thead th sup, thead td sup{
color:#CCDDFF;
}

View File

@@ -342,35 +342,44 @@ ORDER BY ORDINAL_POSITION", null, "") as $row) { //! requires MySQL 5
function selectSearchProcess($fields, $indexes) {
global $driver;
$return = array();
$return = [];
foreach ((array) $_GET["where"] as $key => $where) {
$col = $where["col"];
$op = $where["op"];
$val = $where["val"];
if (($key < 0 ? "" : $col) . $val != "") {
$conds = array();
foreach (($col != "" ? array($col => $fields[$col]) : $fields) as $name => $field) {
if ($col != "" || is_numeric($val) || !preg_match(number_type(), $field["type"])) {
$name = idf_escape($name);
if ($col != "" && $field["type"] == "enum") {
$conds[] = (in_array(0, $val) ? "$name IS NULL OR " : "") . "$name IN (" . implode(", ", array_map('intval', $val)) . ")";
} else {
$text_type = preg_match('~char|text|enum|set~', $field["type"]);
$value = $this->processInput($field, (!$op && $text_type && preg_match('~^[^%]+$~', $val) ? "%$val%" : $val));
$conds[] = $driver->convertSearch($name, $val, $field) . ($value == "NULL" ? " IS" . ($op == ">=" ? " NOT" : "") . " $value"
$conds[] = $driver->convertSearch($name, $where, $field) . ($value == "NULL" ? " IS" . ($op == ">=" ? " NOT" : "") . " $value"
: (in_array($op, $this->operators) || $op == "=" ? " $op $value"
: ($text_type ? " LIKE $value"
: " IN (" . str_replace(",", "', '", $value) . ")"
)));
if ($key < 0 && $val == "0") {
$conds[] = "$name IS NULL";
}
}
}
}
$return[] = ($conds ? "(" . implode(" OR ", $conds) . ")" : "1 = 0");
}
}
return $return;
}

View File

@@ -2,7 +2,7 @@
<?php
error_reporting(6135); // errors and warnings
unset($_COOKIE["adminer_lang"]);
$_SESSION["lang"] = $_SERVER["argv"][1]; // Adminer functions read language from session
$_SESSION["lang"] = isset($_SERVER["argv"][1]) ? $_SERVER["argv"][1] : null; // Adminer functions read language from session
if (isset($_SESSION["lang"])) {
include dirname(__FILE__) . "/adminer/include/lang.inc.php";
if (isset($_SERVER["argv"][2]) || (!isset($langs[$_SESSION["lang"]]) && $_SESSION["lang"] != "xx")) {
@@ -17,7 +17,9 @@ foreach (array_merge(
glob(dirname(__FILE__) . "/adminer/include/*.php"),
glob(dirname(__FILE__) . "/adminer/drivers/*.php"),
glob(dirname(__FILE__) . "/editor/*.php"),
glob(dirname(__FILE__) . "/editor/include/*.php")
glob(dirname(__FILE__) . "/editor/include/*.php"),
glob(dirname(__FILE__) . "/plugins/*.php"),
glob(dirname(__FILE__) . "/plugins/drivers/*.php")
) as $filename) {
$file = file_get_contents($filename);
if (preg_match_all("~lang\\(('(?:[^\\\\']+|\\\\.)*')([),])~", $file, $matches)) { // lang() always uses apostrophes

View File

@@ -133,7 +133,7 @@ if (isset($_GET["elastic"])) {
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
$data = array();
if ($select != array("*")) {
$data["fields"] = $select;
$data["fields"] = array_values($select);
}
if ($order) {

View File

@@ -132,7 +132,7 @@ if (isset($_GET["elastic5"])) {
function select($table, $select, $where, $group, $order = array(), $limit = 1, $page = 0, $print = false) {
$data = array();
if ($select != array("*")) {
$data["fields"] = $select;
$data["fields"] = array_values($select);
}
if ($order) {

View File

@@ -1,43 +1,75 @@
<?php
/** Select foreign key in edit form
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerEditForeign {
var $_limit;
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerEditForeign
{
private $limit;
private $foreignTables = [];
private $foreignOptions = [];
function __construct($limit = 0) {
$this->_limit = $limit;
/**
* @param int $limit
*/
function __construct($limit = 0)
{
$this->limit = $limit;
}
function editInput($table, $field, $attrs, $value) {
static $foreignTables = array();
static $values = array();
$foreignKeys = &$foreignTables[$table];
if ($foreignKeys === null) {
$foreignKeys = column_foreign_keys($table);
/**
* @param string $table
* @param array $field
* @param string $attrs
* @param string|null $value
*
* @return string
*/
function editInput($table, array $field, $attrs, $value)
{
if (!isset($this->foreignTables[$table])) {
$this->foreignTables[$table] = column_foreign_keys($table);
}
foreach ((array) $foreignKeys[$field["field"]] as $foreignKey) {
if (count($foreignKey["source"]) == 1) {
$foreignKeys = $this->foreignTables[$table];
if (empty($foreignKeys[$field["field"]])) {
return "";
}
foreach ($foreignKeys[$field["field"]] as $foreignKey) {
if (count($foreignKey["source"]) != 1) {
continue;
}
$target = $foreignKey["table"];
$id = $foreignKey["target"][0];
$options = &$values[$target][$id];
if (!$options) {
if (!isset($this->foreignOptions[$target][$id])) {
$column = idf_escape($id);
if (preg_match('~binary~', $field["type"])) {
$column = "HEX($column)";
}
$options = array("" => "") + get_vals("SELECT $column FROM " . table($target) . " ORDER BY 1" . ($this->_limit ? " LIMIT " . ($this->_limit + 1) : ""));
if ($this->_limit && count($options) - 1 > $this->_limit) {
return;
}
}
return "<select$attrs>" . optionlist($options, $value) . "</select>";
}
$values = get_vals("SELECT $column FROM " . table($target) . " ORDER BY 1" .
($this->limit ? " LIMIT " . ($this->limit + 1) : ""));
if ($this->limit && count($values) > $this->limit) {
$this->foreignOptions[$target][$id] = false;
} else {
$this->foreignOptions[$target][$id] = ["" => ""] + $values;
}
}
if ($options = $this->foreignOptions[$target][$id]) {
return "<select$attrs>" . optionlist($options, $value) . "</select>";
} else {
return "";
}
}
return "";
}
}

View File

@@ -10,7 +10,7 @@ class AdminerEditTextarea {
function editInput($table, $field, $attrs, $value) {
if (preg_match('~char~', $field["type"])) {
return "<textarea cols='30' rows='1' style='height: 1.2em;'$attrs>" . h($value) . '</textarea>';
return "<textarea cols='30' rows='1' $attrs>" . h($value) . '</textarea>';
}
}

View File

@@ -24,7 +24,9 @@ class AdminerEnumOption {
$selected = "";
}
}
if (!is_strict_mode()) {
$options[0] = lang('empty');
}
preg_match_all("~'((?:[^']|'')*)'~", $field["length"], $matches);
foreach ($matches[1] as $i => $val) {
$val = stripcslashes(str_replace("''", "'", $val));

View File

@@ -1,54 +1,79 @@
<?php
/** Require One-Time Password at login
* @link https://www.adminer.org/plugins/otp/
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerLoginOtp {
/** @access protected */
var $secret;
* @link https://www.adminer.org/plugins/otp/
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerLoginOtp
{
/** @var string */
private $secret;
/**
* @param string decoded secret, e.g. base32_decode("SECRET")
* @param string $secret Decoded secret, e.g. base64_decode("ENCODED_SECRET").
*/
function __construct($secret) {
public function __construct($secret) {
$this->secret = $secret;
if ($_POST["auth"]) {
$_SESSION["otp"] = (string) $_POST["auth"]["otp"];
if (isset($_POST["auth"])) {
$_SESSION["otp"] = (string)$_POST["auth"]["otp"];
}
}
function loginFormField($name, $heading, $value) {
if ($name == 'password') {
return $heading . $value
. "<tr><th><acronym title='One Time Password' lang='en'>OTP</acronym>"
. "<td><input type='number' name='auth[otp]' value='" . h($_SESSION["otp"]) . "' size='6' autocomplete='off'>\n"
;
}
/**
* @param string $name
* @param string $heading
* @param string $value
*
* @return string|null
*/
public function loginFormField($name, $heading, $value) {
if ($name != "password") return null;
return $heading . $value .
"<tr><th><abbr title='" . lang('One Time Password') . "' lang='en'>OTP</abbr></th>" .
"<td><input type='text' name='auth[otp]' value='" . h($_SESSION["otp"]) . "' " .
"size='6' autocomplete='one-time-code' inputmode='numeric' maxlength='6' pattern='\d{6}'/></td>" .
"</tr>\n";
}
function login($login, $password) {
if (isset($_SESSION["otp"])) {
/**
* @param string $login
* @param string $password
*
* @return string|null
*/
public function login($login, $password) {
if (!isset($_SESSION["otp"])) return null;
$timeSlot = floor(time() / 30);
foreach (array(0, -1, 1) as $skew) {
if ($_SESSION["otp"] == $this->getOtp($timeSlot + $skew)) {
restart_session();
unset($_SESSION["otp"]);
stop_session();
return;
}
}
return 'Invalid OTP.';
return null;
}
}
function getOtp($timeSlot) {
$data = str_pad(pack('N', $timeSlot), 8, "\0", STR_PAD_LEFT);
$hash = hash_hmac('sha1', $data, $this->secret, true);
return lang('Invalid OTP code.');
}
/**
* @param int $timeSlot
*
* @return int
*/
private function getOtp($timeSlot) {
$data = str_pad(pack("N", $timeSlot), 8, "\0", STR_PAD_LEFT);
$hash = hash_hmac("sha1", $data, $this->secret, true);
$offset = ord(substr($hash, -1)) & 0xF;
$unpacked = unpack('N', substr($hash, $offset, 4));
$unpacked = unpack("N", substr($hash, $offset, 4));
return ($unpacked[1] & 0x7FFFFFFF) % 1e6;
}
}

View File

@@ -1,24 +1,28 @@
<?php
/** Connect to MySQL using SSL
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerLoginSsl {
/** @access protected */
var $ssl;
/**
* Connect to MySQL using SSL
*
* @link https://www.adminer.org/plugins/#use
* @author Jakub Vrana, https://www.vrana.cz/
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
class AdminerLoginSsl
{
private var $ssl;
/**
* @param array array("key" => filename, "cert" => filename, "ca" => filename)
* MySQL: ["key" => filename, "cert" => filename, "ca" => filename]
* PostgresSQL: ["mode" => sslmode] (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLMODE)
*/
function __construct($ssl) {
function __construct(array $ssl)
{
$this->ssl = $ssl;
}
function connectSsl() {
function connectSsl()
{
return $this->ssl;
}
}