mirror of
https://github.com/vrana/adminer.git
synced 2026-03-25 14:00:05 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaad45a781 | ||
|
|
9a5b8f1f92 | ||
|
|
1b43a6f034 | ||
|
|
fdc2326376 | ||
|
|
08f93d6d09 | ||
|
|
4fbe8ebf5a | ||
|
|
86285dcf34 | ||
|
|
26c4057946 | ||
|
|
d15d0b2ef3 | ||
|
|
bd1dffe086 | ||
|
|
4918ba407f | ||
|
|
46638ebd9a |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -13,5 +13,6 @@ jobs:
|
||||
- uses: php-actions/composer@v6
|
||||
- uses: php-actions/phpcs@v1
|
||||
with:
|
||||
path: adminer/
|
||||
path: .
|
||||
standard: phpcs.xml
|
||||
- uses: php-actions/phpstan@v3
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,3 +1,9 @@
|
||||
## Adminer 5.4.1 (released 2025-09-26)
|
||||
- SQL command: Unlink NULL primary keys
|
||||
- Do not quote 0 in CSV export
|
||||
- Warn about exceeded upload_max_filesize in imports
|
||||
- MySQL: Fix displaying routine definition (bug #1156, regression from 5.4.0)
|
||||
|
||||
## Adminer 5.4.0 (released 2025-09-08)
|
||||
- Allow specifying operator in search anywhere
|
||||
- Do not order descending in GROUP BY select
|
||||
@@ -25,7 +31,7 @@
|
||||
- PostgreSQL: Fix calling functions returing table
|
||||
- PostgreSQL: Don't treat user types containing 'file' as blobs (bug #1118)
|
||||
- PostgreSQL: Export DROP and CREATE DATABASE (bug #1140)
|
||||
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089)
|
||||
- PostgreSQL 11-: Avoid duplicate oid in table status (bug #1089, regression from 5.3.0)
|
||||
- Elasticsearch: Support dropping aliases
|
||||
- Plugins: Methods afterConnect(), processList() and killProcess()
|
||||
- New plugin: Display row numbers in select (bug #1106)
|
||||
@@ -149,8 +155,8 @@
|
||||
- MariaDB: Fix creating and altering generated columns (bug #897)
|
||||
- PostgreSQL: Fix "where" and "order" privileges (bug #902, regression from 5.0.2)
|
||||
- SQLite: Fix creating table in compiled version (bug #901, regression from 5.0.0)
|
||||
- Elastic: Do not pass null values on insert (PR #892)
|
||||
- Elastic: Fix displaying sparse rows (PR #893)
|
||||
- Elasticsearch: Do not pass null values on insert (PR #892)
|
||||
- Elasticsearch: Fix displaying sparse rows (PR #893)
|
||||
- Plugins: Add method dumpFooter()
|
||||
|
||||
## Adminer 5.0.2 (released 2025-03-10)
|
||||
@@ -231,6 +237,7 @@
|
||||
- SQLite: Fix expressions in default values (bug SF-860)
|
||||
- MS SQL: Foreign keys in non-default schema (bug SF-833)
|
||||
- Oracle: Include tables granted by other user
|
||||
- Elasticsearch: Move to plugin
|
||||
- MongoDB: Execute commands against the selected DB
|
||||
|
||||
## Adminer 4.15.0
|
||||
|
||||
@@ -897,7 +897,7 @@ if (!defined('Adminer\DRIVER')) {
|
||||
$space = "(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";
|
||||
$enum = driver()->enumLength;
|
||||
$type_pattern = "((" . implode("|", array_merge(array_keys(driver()->types()), $aliases)) . ")\\b(?:\\s*\\(((?:[^'\")]|$enum)++)\\))?"
|
||||
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?(?:\\s*COLLATE\\s*['\"]?([^'\"\\s,]+)['\"]?)?"; //! store COLLATE
|
||||
. "\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?(?:\\s*COLLATE\\s*['\"]?[^'\"\\s,]+['\"]?)?"; //! store COLLATE
|
||||
$pattern = "$space*(" . ($type == "FUNCTION" ? "" : driver()->inout) . ")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$type_pattern";
|
||||
$create = get_val("SHOW CREATE $type " . idf_escape($name), 2);
|
||||
preg_match("~\\(((?:$pattern\\s*,?)*)\\)\\s*" . ($type == "FUNCTION" ? "RETURNS\\s+$type_pattern\\s+" : "") . "(.*)~is", $create, $match);
|
||||
|
||||
@@ -72,6 +72,10 @@ function print_select_result($result, ?Db $connection2 = null, array $orgtables
|
||||
} else {
|
||||
$link = ME . "edit=" . urlencode($links[$key]);
|
||||
foreach ($indexes[$links[$key]] as $col => $j) {
|
||||
if ($row[$j] === null) {
|
||||
$link = "";
|
||||
break;
|
||||
}
|
||||
$link .= "&where" . urlencode("[" . bracket_escape($col) . "]") . "=" . urlencode($row[$j]);
|
||||
}
|
||||
}
|
||||
@@ -507,20 +511,6 @@ function tar_file(string $filename, $tmp_file): void {
|
||||
echo str_repeat("\0", 511 - ($tmp_file->size + 511) % 512);
|
||||
}
|
||||
|
||||
/** Get INI bytes value */
|
||||
function ini_bytes(string $ini): int {
|
||||
$val = ini_get($ini);
|
||||
switch (strtolower(substr($val, -1))) {
|
||||
case 'g':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'm':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'k':
|
||||
$val = (int) $val * 1024;
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/** Create link to database documentation
|
||||
* @param string[] $paths JUSH => $path
|
||||
* @param string $text HTML code
|
||||
|
||||
@@ -128,6 +128,20 @@ function ini_bool(string $ini): bool {
|
||||
return (preg_match('~^(on|true|yes)$~i', $val) || (int) $val); // boolean values set by php_value are strings
|
||||
}
|
||||
|
||||
/** Get INI bytes value */
|
||||
function ini_bytes(string $ini): int {
|
||||
$val = ini_get($ini);
|
||||
switch (strtolower(substr($val, -1))) {
|
||||
case 'g':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'm':
|
||||
$val = (int) $val * 1024; // no break
|
||||
case 'k':
|
||||
$val = (int) $val * 1024;
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/** Check if SID is necessary */
|
||||
function sid(): bool {
|
||||
static $return;
|
||||
@@ -643,7 +657,7 @@ function dump_headers(string $identifier, bool $multi_table = false): string {
|
||||
*/
|
||||
function dump_csv(array $row): void {
|
||||
foreach ($row as $key => $val) {
|
||||
if (preg_match('~["\n,;\t]|^0|\.\d*0$~', $val) || $val === "") {
|
||||
if (preg_match('~["\n,;\t]|^0.|\.\d*0$~', $val) || $val === "") {
|
||||
$row[$key] = '"' . str_replace('"', '""', $val) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,20 @@ function hidden_fields_get(): void {
|
||||
echo input_hidden("username", $_GET["username"]);
|
||||
}
|
||||
|
||||
/** Get <input type='file'> */
|
||||
function file_input(string $input): string {
|
||||
$max_file_uploads = "max_file_uploads";
|
||||
$max_file_uploads_value = ini_get($max_file_uploads);
|
||||
$upload_max_filesize = "upload_max_filesize";
|
||||
$upload_max_filesize_value = ini_get($upload_max_filesize);
|
||||
return (ini_bool("file_uploads")
|
||||
? $input . script("qsl('input[type=\"file\"]').onchange = partialArg(fileChange, "
|
||||
. "$max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "', " // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
|
||||
. ini_bytes("upload_max_filesize") . ", '" . lang('Increase %s.', "$upload_max_filesize = $upload_max_filesize_value") . "')")
|
||||
: lang('File uploads are disabled.')
|
||||
);
|
||||
}
|
||||
|
||||
/** Print enum or set input field
|
||||
* @param 'radio'|'checkbox' $type
|
||||
* @param Field $field
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
namespace Adminer;
|
||||
|
||||
const VERSION = "5.4.0";
|
||||
const VERSION = "5.4.1";
|
||||
|
||||
@@ -595,9 +595,10 @@ if (!$columns && support("table")) {
|
||||
echo "<a href='#import'>" . lang('Import') . "</a>";
|
||||
echo script("qsl('a').onclick = partial(toggle, 'import');", "");
|
||||
echo "<span id='import'" . ($_POST["import"] ? "" : " class='hidden'") . ">: ";
|
||||
echo "<input type='file' name='csv_file'> ";
|
||||
echo html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"]);
|
||||
echo " <input type='submit' name='import' value='" . lang('Import') . "'>";
|
||||
echo file_input("<input type='file' name='csv_file'> "
|
||||
. html_select("separator", array("csv" => "CSV,", "csv;" => "CSV;", "tsv" => "TSV"), $adminer_import["format"])
|
||||
. " <input type='submit' name='import' value='" . lang('Import') . "'>")
|
||||
;
|
||||
echo "</span>";
|
||||
}
|
||||
|
||||
|
||||
@@ -250,15 +250,9 @@ if (!isset($_GET["import"])) {
|
||||
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";
|
||||
|
||||
} else {
|
||||
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
|
||||
$gz = (extension_loaded("zlib") ? "[.gz]" : "");
|
||||
$max_file_uploads = "max_file_uploads";
|
||||
$max_file_uploads_value = ini_get($max_file_uploads);
|
||||
echo (ini_bool("file_uploads")
|
||||
? "SQL$gz (< " . ini_get("upload_max_filesize") . "B): <input type='file' name='sql_file[]' multiple>\n" // ignore post_max_size because it is for all form fields together and bytes computing would be necessary
|
||||
. script("qsl('input').onchange = partialArg(fileChange, $max_file_uploads_value, '" . lang('Increase %s.', "$max_file_uploads = $max_file_uploads_value") . "')") . $execute
|
||||
: lang('File uploads are disabled.')
|
||||
);
|
||||
echo "<fieldset><legend>" . lang('File upload') . "</legend><div>";
|
||||
echo file_input("SQL$gz: <input type='file' name='sql_file[]' multiple>\n$execute");
|
||||
echo "</div></fieldset>\n";
|
||||
$importServerPath = adminer()->importServerPath();
|
||||
if ($importServerPath) {
|
||||
|
||||
@@ -662,24 +662,29 @@ function indexesAddColumn(prefix) {
|
||||
* @param string
|
||||
*/
|
||||
function sqlSubmit(form, root) {
|
||||
if (encodeURIComponent(form['query'].value).length < 500) {
|
||||
form.action = root
|
||||
+ '&sql=' + encodeURIComponent(form['query'].value)
|
||||
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '')
|
||||
+ (form['error_stops'].checked ? '&error_stops=1' : '')
|
||||
+ (form['only_errors'].checked ? '&only_errors=1' : '')
|
||||
;
|
||||
const action = root
|
||||
+ '&sql=' + encodeURIComponent(form['query'].value)
|
||||
+ (form['limit'].value ? '&limit=' + +form['limit'].value : '')
|
||||
+ (form['error_stops'].checked ? '&error_stops=1' : '')
|
||||
+ (form['only_errors'].checked ? '&only_errors=1' : '')
|
||||
;
|
||||
if ((document.location.origin + document.location.pathname + action).length < 2000) { // reasonable minimum is 2048
|
||||
form.action = action;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if PHP can handle the uploaded files
|
||||
* @param Event
|
||||
* @param number
|
||||
* @param string
|
||||
* @param number
|
||||
* @param string
|
||||
* @param Event
|
||||
*/
|
||||
function fileChange(event, maxFileUploads, message) {
|
||||
if (event.target.files.length > maxFileUploads) {
|
||||
alert(message);
|
||||
function fileChange(event, count, countMessage, size, sizeMessage) {
|
||||
if (event.target.files.length > count) {
|
||||
alert(countMessage);
|
||||
} else if (Array.from(event.target.files).reduce((sum, file) => sum + file.size, 0) > size) {
|
||||
alert(sizeMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -f adminer*.php editor*.php",
|
||||
"compile": "@php compile.php"
|
||||
"check": [
|
||||
"phpcs",
|
||||
"phpstan analyse -c phpstan.neon"
|
||||
],
|
||||
"compile": "@php compile.php",
|
||||
"clean": "rm -f adminer*.php editor*.php"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,3 @@ function whisperClick(event) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Add new attachment field
|
||||
* @this HTMLInputElement
|
||||
*/
|
||||
function emailFileChange() {
|
||||
const el = this.cloneNode(true);
|
||||
this.onchange = () => { };
|
||||
el.value = '';
|
||||
this.parentNode.appendChild(el);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user