Compare commits

...

12 Commits

Author SHA1 Message Date
Jakub Vrana
eaad45a781 Release 5.4.1 2025-09-26 17:38:02 +02:00
Jakub Vrana
9a5b8f1f92 Do not quote 0 in CSV export 2025-09-16 10:18:07 +02:00
Jakub Vrana
1b43a6f034 Warn about exceeded upload_max_filesize in imports 2025-09-15 20:12:30 +02:00
Jakub Vrana
fdc2326376 Remove unused function 2025-09-15 19:51:56 +02:00
Jakub Vrana
08f93d6d09 Save bytes 2025-09-12 07:42:10 +02:00
Jakub Vrana
4fbe8ebf5a Update changelog 2025-09-11 15:10:07 +02:00
Jakub Vrana
86285dcf34 MySQL: Fix displaying routine definition (fix #1156, regression from 5.4.0) 2025-09-11 14:35:48 +02:00
Jakub Vrana
26c4057946 SQL command: Unlink NULL primary keys 2025-09-10 00:23:26 +02:00
Jakub Vrana
d15d0b2ef3 Add Composer script check 2025-09-10 00:17:44 +02:00
Jakub Vrána
bd1dffe086 CI: Run PHPStan 2025-09-09 08:28:20 +02:00
Jakub Vrana
4918ba407f Increase max. ?sql= length (fix #1154) 2025-09-08 21:30:44 +02:00
Jakub Vrana
46638ebd9a Develop 2025-09-08 11:43:22 +02:00
12 changed files with 75 additions and 55 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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) . '"';
}
}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
<?php
namespace Adminer;
const VERSION = "5.4.0";
const VERSION = "5.4.1";

View File

@@ -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>";
}

View File

@@ -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 (&lt; " . 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) {

View File

@@ -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);
}
}

View File

@@ -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"
}
}

View File

@@ -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);
}