Merge branch 'QA_5_2'

Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2026-02-24 12:28:24 +01:00
commit 015d75c0d1
No known key found for this signature in database
GPG Key ID: 70684F4717D49A31
47 changed files with 387 additions and 129 deletions

View File

@ -13,11 +13,28 @@ permissions:
env: env:
php-version: "8.2" php-version: "8.2"
node-version: "20"
jobs: jobs:
generate-matrix:
name: Generate the matrix of Selenium tests
if: "!contains(github.event.head_commit.message, '[ci selenium skip]')"
runs-on: ubuntu-latest
outputs:
tests: ${{ steps.tests-list.outputs.tests }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- id: tests-list
name: Send test names to outputs
run: |
set -euo pipefail
TEST_NAMES="[$(find $PWD/tests/end-to-end/ -type f -name '*Test.php' -print |sed -E "s,$PWD/tests/end-to-end/(.*)Test.php,{\"test-name\": \"\1\"}," | paste -sd ",")]"
echo "tests=$(printf "$TEST_NAMES")" >> $GITHUB_OUTPUT
selenium: selenium:
name: "Selenium" name: "Selenium"
if: "!contains(github.event.head_commit.message, '[ci selenium skip]')" if: "!contains(github.event.head_commit.message, '[ci selenium skip]')"
needs: generate-matrix
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
services: services:
database-server: database-server:
@ -58,28 +75,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
test-name: include: ${{ fromJSON(needs.generate-matrix.outputs.tests) }}
- "ChangePassword"
- "CreateDropDatabase"
- "CreateRemoveUser"
- "Database/Events"
- "Database/Operations"
- "Database/Procedures"
- "Database/Structure"
- "Export"
- "Import"
- "Login"
- "Normalization"
- "ServerSettings"
- "SqlQuery"
- "Table/Browse"
- "Table/Create"
- "Table/Insert"
- "Table/Operations"
- "Table/Structure"
- "Tracking"
- "Triggers"
- "Xss"
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v6
@ -103,7 +99,7 @@ jobs:
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: '20' node-version: '${{ env.node-version }}'
cache: 'yarn' cache: 'yarn'
- name: Install modules - name: Install modules

View File

@ -7092,7 +7092,7 @@ parameters:
- -
message: '#^Cannot cast mixed to string\.$#' message: '#^Cannot cast mixed to string\.$#'
identifier: cast.string identifier: cast.string
count: 6 count: 7
path: src/Import/Import.php path: src/Import/Import.php
- -
@ -7125,6 +7125,12 @@ parameters:
count: 1 count: 1
path: src/Import/Import.php path: src/Import/Import.php
-
message: '#^Only booleans are allowed in a negated boolean, int\|false given\.$#'
identifier: booleanNot.exprNotBoolean
count: 1
path: src/Import/Import.php
- -
message: '#^Parameter \#1 \$precision of static method PhpMyAdmin\\Import\\DecimalSize\:\:fromPrecisionAndScale\(\) expects int, int\|PhpMyAdmin\\Import\\DecimalSize given\.$#' message: '#^Parameter \#1 \$precision of static method PhpMyAdmin\\Import\\DecimalSize\:\:fromPrecisionAndScale\(\) expects int, int\|PhpMyAdmin\\Import\\DecimalSize given\.$#'
identifier: argument.type identifier: argument.type
@ -8634,24 +8640,6 @@ parameters:
count: 1 count: 1
path: src/Plugins/Export/ExportJson.php path: src/Plugins/Export/ExportJson.php
-
message: '#^Cannot call method fetchAssoc\(\) on PhpMyAdmin\\Dbal\\ResultInterface\|false\.$#'
identifier: method.nonObject
count: 1
path: src/Plugins/Export/ExportLatex.php
-
message: '#^Cannot call method getFieldNames\(\) on PhpMyAdmin\\Dbal\\ResultInterface\|false\.$#'
identifier: method.nonObject
count: 1
path: src/Plugins/Export/ExportLatex.php
-
message: '#^Cannot call method numFields\(\) on PhpMyAdmin\\Dbal\\ResultInterface\|false\.$#'
identifier: method.nonObject
count: 1
path: src/Plugins/Export/ExportLatex.php
- -
message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
identifier: empty.notAllowed identifier: empty.notAllowed

View File

@ -1316,6 +1316,7 @@
</RiskyTruthyFalsyComparison> </RiskyTruthyFalsyComparison>
<UnusedVariable> <UnusedVariable>
<code><![CDATA[$collationsList]]></code> <code><![CDATA[$collationsList]]></code>
<code><![CDATA[$encryptionKeyLength]]></code>
</UnusedVariable> </UnusedVariable>
</file> </file>
<file src="src/Controllers/Import/ImportController.php"> <file src="src/Controllers/Import/ImportController.php">
@ -4048,9 +4049,6 @@
<code><![CDATA[$_POST['url']]]></code> <code><![CDATA[$_POST['url']]]></code>
<code><![CDATA[$exception['url']]]></code> <code><![CDATA[$exception['url']]]></code>
</PossiblyInvalidCast> </PossiblyInvalidCast>
<PossiblyUndefinedArrayOffset>
<code><![CDATA[$_SERVER['HTTP_USER_AGENT']]]></code>
</PossiblyUndefinedArrayOffset>
<RiskyTruthyFalsyComparison> <RiskyTruthyFalsyComparison>
<code><![CDATA[empty($_POST['description'])]]></code> <code><![CDATA[empty($_POST['description'])]]></code>
<code><![CDATA[empty($_POST['exception'])]]></code> <code><![CDATA[empty($_POST['exception'])]]></code>
@ -4973,6 +4971,9 @@
<code><![CDATA[$lastCumulativeSize->scale]]></code> <code><![CDATA[$lastCumulativeSize->scale]]></code>
<code><![CDATA[$lastCumulativeSize->scale]]></code> <code><![CDATA[$lastCumulativeSize->scale]]></code>
</PossiblyInvalidPropertyFetch> </PossiblyInvalidPropertyFetch>
<PossiblyUndefinedArrayOffset>
<code><![CDATA[$row[$columnIndex]]]></code>
</PossiblyUndefinedArrayOffset>
<RiskyTruthyFalsyComparison> <RiskyTruthyFalsyComparison>
<code><![CDATA[ImportSettings::$runQuery]]></code> <code><![CDATA[ImportSettings::$runQuery]]></code>
</RiskyTruthyFalsyComparison> </RiskyTruthyFalsyComparison>
@ -5820,16 +5821,12 @@
<MixedOperand> <MixedOperand>
<code><![CDATA[$comments[$fieldName]]]></code> <code><![CDATA[$comments[$fieldName]]]></code>
</MixedOperand> </MixedOperand>
<PossiblyFalseReference>
<code><![CDATA[fetchAssoc]]></code>
<code><![CDATA[getFieldNames]]></code>
<code><![CDATA[numFields]]></code>
</PossiblyFalseReference>
<PossiblyInvalidOperand> <PossiblyInvalidOperand>
<code><![CDATA[str_replace('_', '/', $mimeMap[$fieldName]['mimetype'])]]></code> <code><![CDATA[str_replace('_', '/', $mimeMap[$fieldName]['mimetype'])]]></code>
</PossiblyInvalidOperand> </PossiblyInvalidOperand>
<PossiblyNullArgument> <PossiblyNullArgument>
<code><![CDATA[$record[$columns[$i]]]]></code> <code><![CDATA[$record[$columns[$i]]]]></code>
<code><![CDATA[$record[$columns[$i]]]]></code>
</PossiblyNullArgument> </PossiblyNullArgument>
<RiskyTruthyFalsyComparison> <RiskyTruthyFalsyComparison>
<code><![CDATA[empty($this->config->selectedServer['port'])]]></code> <code><![CDATA[empty($this->config->selectedServer['port'])]]></code>
@ -5882,6 +5879,11 @@
<code><![CDATA[$pdf]]></code> <code><![CDATA[$pdf]]></code>
</PropertyNotSetInConstructor> </PropertyNotSetInConstructor>
</file> </file>
<file src="src/Plugins/Export/ExportPhparray.php">
<PossiblyNullArgument>
<code><![CDATA[$record[$i]]]></code>
</PossiblyNullArgument>
</file>
<file src="src/Plugins/Export/ExportSql.php"> <file src="src/Plugins/Export/ExportSql.php">
<ArgumentTypeCoercion> <ArgumentTypeCoercion>
<code><![CDATA[$token->value]]></code> <code><![CDATA[$token->value]]></code>

View File

@ -510,6 +510,26 @@ const rect = function (x1, y1, w, h, color) {
}; };
// --------------------------- FULLSCREEN ------------------------------------- // --------------------------- FULLSCREEN -------------------------------------
const requestFullscreen = function (e) {
if (e.requestFullscreen) {
return e.requestFullscreen();
} else if (e.webkitRequestFullscreen) {
return e.webkitRequestFullscreen();
} else if (e.mozRequestFullScreen) {
return e.mozRequestFullScreen();
} else if (e.msRequestFullscreen) {
return e.msRequestFullscreen();
}
};
const fullscreenEnabled = function () {
// eslint-disable-next-line compat/compat
return document.fullscreenEnabled ||
document.webkitFullscreenEnabled ||
document.mozFullScreenEnabled ||
document.msFullscreenEnabled;
};
const toggleFullscreen = function () { const toggleFullscreen = function () {
var valueSent = ''; var valueSent = '';
var $img = $('#toggleFullscreen').find('img'); var $img = $('#toggleFullscreen').find('img');
@ -517,7 +537,7 @@ const toggleFullscreen = function () {
var $content = $('#page_content'); var $content = $('#page_content');
const pageContent = document.getElementById('page_content'); const pageContent = document.getElementById('page_content');
if (! document.fullscreenEnabled) { if (! DesignerMove.fullscreenEnabled()) {
ajaxShowMessage(window.Messages.strFullscreenRequestDenied, null, 'error'); ajaxShowMessage(window.Messages.strFullscreenRequestDenied, null, 'error');
return; return;
@ -534,7 +554,7 @@ const toggleFullscreen = function () {
$('#osn_tab').css({ 'width': screen.width + 'px', 'height': screen.height }); $('#osn_tab').css({ 'width': screen.width + 'px', 'height': screen.height });
valueSent = 'on'; valueSent = 'on';
pageContent.requestFullscreen(); DesignerMove.requestFullscreen(pageContent);
} else { } else {
$img.attr('src', $img.data('enter')) $img.attr('src', $img.data('enter'))
.attr('title', $span.data('enter')); .attr('title', $span.data('enter'));
@ -2077,6 +2097,8 @@ const DesignerMove = {
clear: clear, clear: clear,
rect: rect, rect: rect,
toggleFullscreen: toggleFullscreen, toggleFullscreen: toggleFullscreen,
fullscreenEnabled: fullscreenEnabled,
requestFullscreen: requestFullscreen,
addTableToTablesList: addTableToTablesList, addTableToTablesList: addTableToTablesList,
displayModal: displayModal, displayModal: displayModal,
addOtherDbTables: addOtherDbTables, addOtherDbTables: addOtherDbTables,

View File

@ -8,7 +8,8 @@ import {
getCellValue, getCellValue,
stringifyJSON, stringifyJSON,
toggleDatepickerIfInvalid, toggleDatepickerIfInvalid,
updateCode updateCode,
userAgent
} from './modules/functions.ts'; } from './modules/functions.ts';
import { CommonParams } from './modules/common.ts'; import { CommonParams } from './modules/common.ts';
import highlightSql from './modules/sql-highlight.ts'; import highlightSql from './modules/sql-highlight.ts';
@ -277,7 +278,7 @@ const makeGrid = function (t, enableResize = undefined, enableReorder = undefine
for (var n = 0, l = $firstRowCols.length; n < l; n++) { for (var n = 0, l = $firstRowCols.length; n < l; n++) {
var $col = $($firstRowCols[n]); var $col = $($firstRowCols[n]);
var colWidth; var colWidth;
if (navigator.userAgent.toLowerCase().indexOf('safari') !== -1) { if (userAgent().toLowerCase().indexOf('safari') !== -1) {
colWidth = $col.outerWidth(); colWidth = $col.outerWidth();
} else { } else {
colWidth = $col.outerWidth(true); colWidth = $col.outerWidth(true);
@ -301,6 +302,20 @@ const makeGrid = function (t, enableResize = undefined, enableReorder = undefine
$(g.cRsz).css('height', $(g.t).height()); $(g.cRsz).css('height', $(g.t).height());
}, },
/**
* Clears the current cell edit state, internal flags,
* and any pending save request.
*/
resetGridEditState: function () {
g.isCellEditActive = false;
g.isEditCellTextEditable = false;
g.currentEditCell = null;
g.wasEditedCellNull = false;
g.isCellEdited = false;
g.isSaving = false;
g.lastXHR = null;
},
/** /**
* Shift column from index oldn to newn. * Shift column from index oldn to newn.
* *
@ -2823,6 +2838,9 @@ const makeGrid = function (t, enableResize = undefined, enableReorder = undefine
// some adjustment // some adjustment
$(t).removeClass('data'); $(t).removeClass('data');
$(g.gDiv).addClass('data'); $(g.gDiv).addClass('data');
/* Store the grid controller instance on the table element so it can be accessed later by other modules
(e.g. during AJAX teardown) without exposing the grid object as a global variable.*/
$(t).data('pmaGrid', g);
g.initCellSelection(); g.initCellSelection();
}; };

View File

@ -64,6 +64,19 @@ export function addNoCacheToAjaxRequests (options: JQuery.AjaxSettings, original
} }
} }
/**
* Get an empty string for user-agent, if undefined
*/
export function userAgent (): string {
try {
return navigator.userAgent;
} catch (e) {
console.error(e);
return '';
}
}
/** /**
* Adds a date/time picker to an element * Adds a date/time picker to an element
* *

View File

@ -38,7 +38,8 @@ import {
teardownIdleEvent, teardownIdleEvent,
teardownRecentFavoriteTables, teardownRecentFavoriteTables,
teardownSortLinkMouseEvent, teardownSortLinkMouseEvent,
teardownSqlQueryEditEvents teardownSqlQueryEditEvents,
userAgent,
} from '../functions.ts'; } from '../functions.ts';
import handleCreateViewModal from './handleCreateViewModal.ts'; import handleCreateViewModal from './handleCreateViewModal.ts';
@ -93,7 +94,7 @@ export function onloadFunctions () {
/** /**
* Add attribute to text boxes for iOS devices (based on bugID: 3508912) * Add attribute to text boxes for iOS devices (based on bugID: 3508912)
*/ */
if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) { if (userAgent().match(/(iphone|ipod|ipad)/i)) {
$('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off'); $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off');
} }

View File

@ -462,9 +462,9 @@ export default function highlightSql ($base) {
var $pre = $sql.closest('pre'); var $pre = $sql.closest('pre');
/* We only care about visible elements to avoid double processing */ /* We only care about visible elements to avoid double processing */
if ($sql.is(':visible')) { if ($sql.is(':visible')) {
var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
$pre.append($highlight);
if (typeof window.CodeMirror !== 'undefined') { if (typeof window.CodeMirror !== 'undefined') {
var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
$pre.append($highlight);
// @ts-ignore // @ts-ignore
window.CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]); window.CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]);
$sql.hide(); $sql.hide();

View File

@ -505,6 +505,13 @@ AJAX.registerTeardown('table/change.js', function () {
$(document).off('click', 'input.checkbox_null'); $(document).off('click', 'input.checkbox_null');
$('select[name="submit_type"]').off('change'); $('select[name="submit_type"]').off('change');
$(document).off('change', '#insert_rows'); $(document).off('change', '#insert_rows');
// Reset grid edit state
var grid = $('table.table_results').data('pmaGrid');
if (grid && typeof grid.resetGridEditState === 'function') {
grid.resetGridEditState();
}
}); });
/** /**

View File

@ -343,6 +343,37 @@ final class HomeController implements InvocableController
} }
} }
/**
* Check if user does not have defined query encryption key and it is being used.
*/
if ($this->config->config->URLQueryEncryption) {
$encryptionKeyLength = 0;
$encryptionKeyLength = mb_strlen($this->config->config->URLQueryEncryptionSecretKey, '8bit');
if ($encryptionKeyLength < SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
$this->errors[] = [
'message' => __(
'The configuration file needs a valid key for query encryption.'
. ' A temporary key was automatically generated for you.'
. ' Please refer to the [doc@cfg_URLQueryEncryptionSecretKey]documentation[/doc].',
),
'severity' => 'warning',
];
} elseif ($encryptionKeyLength > SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
$this->errors[] = [
'message' => sprintf(
__(
'The query encryption key in the configuration file is longer than necessary.'
. ' It should only be %d bytes long.'
. ' Please refer to the [doc@cfg_URLQueryEncryptionSecretKey]documentation[/doc].',
),
SODIUM_CRYPTO_SECRETBOX_KEYBYTES,
),
'severity' => 'warning',
];
}
}
/** /**
* Check for existence of config directory which should not exist in * Check for existence of config directory which should not exist in
* production environment. * production environment.

View File

@ -692,7 +692,10 @@ final class JavaScriptMessagesController implements InvocableController
'strHide' => __('Hide'), 'strHide' => __('Hide'),
'strShow' => __('Show'), 'strShow' => __('Show'),
'strStructure' => __('Structure'), 'strStructure' => __('Structure'),
'strFullscreenRequestDenied' => __('The fullscreen request was denied.'), 'strFullscreenRequestDenied' => __(
'The fullscreen request was denied because'
. ' the Fullscreen API is disabled by user preference in the browser.',
),
'strMonthNameJan' => _pgettext('Month name', 'January'), 'strMonthNameJan' => _pgettext('Month name', 'January'),
'strMonthNameFeb' => _pgettext('Month name', 'February'), 'strMonthNameFeb' => _pgettext('Month name', 'February'),

View File

@ -75,7 +75,7 @@ class ErrorReport
'browser_version' => $userAgentParser->getUserBrowserVersion(), 'browser_version' => $userAgentParser->getUserBrowserVersion(),
'user_os' => $userAgentParser->getClientPlatform(), 'user_os' => $userAgentParser->getClientPlatform(),
'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? null, 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? null,
'user_agent_string' => $_SERVER['HTTP_USER_AGENT'], 'user_agent_string' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'locale' => $this->config->getCookie('pma_lang'), 'locale' => $this->config->getCookie('pma_lang'),
'configuration_storage' => $relationParameters->db === null ? 'disabled' : 'enabled', 'configuration_storage' => $relationParameters->db === null ? 'disabled' : 'enabled',
'php_version' => PHP_VERSION, 'php_version' => PHP_VERSION,

View File

@ -217,10 +217,12 @@ class Header
$themeManager = ContainerBuilder::getContainer()->get(ThemeManager::class); $themeManager = ContainerBuilder::getContainer()->get(ThemeManager::class);
$theme = $themeManager->theme; $theme = $themeManager->theme;
$scripts = $this->getScripts(); $scripts = $this->getScripts();
$userAgent = Core::getEnv('HTTP_USER_AGENT');
// The user preferences have been merged at this point // The user preferences have been merged at this point
// so we can conditionally add CodeMirror, other scripts and settings // so we can conditionally add CodeMirror, other scripts and settings
if ($this->config->config->CodemirrorEnable) { // See #20159, why we need to check for HTTP_USER_AGENT here
if ($this->config->config->CodemirrorEnable && $userAgent !== '') {
$scripts->addFile('vendor/codemirror/lib/codemirror.js'); $scripts->addFile('vendor/codemirror/lib/codemirror.js');
$scripts->addFile('vendor/codemirror/mode/sql/sql.js'); $scripts->addFile('vendor/codemirror/mode/sql/sql.js');
$scripts->addFile('vendor/codemirror/addon/runmode/runmode.js'); $scripts->addFile('vendor/codemirror/addon/runmode/runmode.js');

View File

@ -565,7 +565,7 @@ class Generator
if ( if (
! empty($config->settings['SQLQuery']['Refresh']) ! empty($config->settings['SQLQuery']['Refresh'])
&& Sql::$showAsPhp === null // 'Submit query' does the same && Sql::$showAsPhp === null // 'Submit query' does the same
&& preg_match('@^(SELECT|SHOW)[[:space:]]+@i', $sqlQuery) === 1 && preg_match('@^(ANALYZE|EXPLAIN|SELECT|SHOW)[[:space:]]+@i', $sqlQuery) === 1
) { ) {
$refreshLink = Url::getFromRoute('/sql', $urlParams); $refreshLink = Url::getFromRoute('/sql', $urlParams);
$refreshLink = '<div class="col-auto">' $refreshLink = '<div class="col-auto">'
@ -954,6 +954,7 @@ class Generator
// Has as sql_query without a signature, to be accepted it needs to be sent using POST // Has as sql_query without a signature, to be accepted it needs to be sent using POST
str_contains($url, 'sql_query=') && ! str_contains($url, 'sql_signature=') str_contains($url, 'sql_query=') && ! str_contains($url, 'sql_signature=')
) )
|| $config->config->URLQueryEncryption
|| str_contains($url, 'view[as]='); || str_contains($url, 'view[as]=');
if ($respectUrlLengthLimit && $isDataPostFormatSupported) { if ($respectUrlLengthLimit && $isDataPostFormatSupported) {
$parts = explode('?', $url, 2); $parts = explode('?', $url, 2);

View File

@ -847,7 +847,8 @@ final class Import
if ($analyses !== null) { if ($analyses !== null) {
$isVarchar = $analyses[$tableIndex][$columnIndex]->type === ColumnType::Varchar; $isVarchar = $analyses[$tableIndex][$columnIndex]->type === ColumnType::Varchar;
} else { } else {
$isVarchar = ! is_numeric($row[$columnIndex]); $isVarchar = ! is_numeric($row[$columnIndex])
&& ! preg_match('/^0x[0-9a-f]+$/', (string) $row[$columnIndex]);
} }
/* Don't put quotes around NULL fields */ /* Don't put quotes around NULL fields */

View File

@ -21,6 +21,7 @@ use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties; use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __; use function __;
use function bin2hex;
use function implode; use function implode;
use function is_string; use function is_string;
use function str_replace; use function str_replace;
@ -139,6 +140,7 @@ class ExportCsv extends ExportPlugin
array $aliases = [], array $aliases = [],
): void { ): void {
$result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$fieldsMeta = $this->dbi->getFieldsMeta($result);
$charsNeedingEnclosure = $this->separator . $this->enclosed . $this->terminated; $charsNeedingEnclosure = $this->separator . $this->enclosed . $this->terminated;
@ -165,7 +167,7 @@ class ExportCsv extends ExportPlugin
// Format the data // Format the data
while ($row = $result->fetchRow()) { while ($row = $result->fetchRow()) {
$insertValues = []; $insertValues = [];
foreach ($row as $field) { foreach ($row as $j => $field) {
if ($field === null) { if ($field === null) {
$insertValues[] = $this->null; $insertValues[] = $this->null;
continue; continue;
@ -176,6 +178,11 @@ class ExportCsv extends ExportPlugin
continue; continue;
} }
if ($fieldsMeta[$j]->isMappedTypeGeometry || $fieldsMeta[$j]->isBinary) {
$insertValues[] = '0x' . bin2hex($row[$j] ?? '');
continue;
}
// remove CRLF characters within field // remove CRLF characters within field
if ($this->removeCrLf) { if ($this->removeCrLf) {
$field = str_replace(["\r", "\n"], '', $field); $field = str_replace(["\r", "\n"], '', $field);

View File

@ -28,6 +28,8 @@ use function __;
use function addcslashes; use function addcslashes;
use function array_keys; use function array_keys;
use function array_values; use function array_values;
use function assert;
use function bin2hex;
use function in_array; use function in_array;
use function is_string; use function is_string;
use function mb_strpos; use function mb_strpos;
@ -272,8 +274,10 @@ class ExportLatex extends ExportPlugin
$tableAlias = $this->getTableAlias($aliases, $db, $table); $tableAlias = $this->getTableAlias($aliases, $db, $table);
$result = $this->dbi->tryQuery($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->tryQuery($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
assert($result !== false);
$columnsCnt = $result->numFields(); $columnsCnt = $result->numFields();
$fieldsMeta = $this->dbi->getFieldsMeta($result);
$columns = []; $columns = [];
$columnsAlias = []; $columnsAlias = [];
foreach ($result->getFieldNames() as $i => $colAs) { foreach ($result->getFieldNames() as $i => $colAs) {
@ -350,7 +354,13 @@ class ExportLatex extends ExportPlugin
/** @infection-ignore-all */ /** @infection-ignore-all */
for ($i = 0; $i < $columnsCnt; $i++) { for ($i = 0; $i < $columnsCnt; $i++) {
if ($record[$columns[$i]] !== null) { if ($record[$columns[$i]] !== null) {
$columnValue = self::texEscape($record[$columns[$i]]); if ($fieldsMeta[$i]->isMappedTypeGeometry || $fieldsMeta[$i]->isBinary) {
$columnValue = self::texEscape(
'0x' . bin2hex($record[$columns[$i]]),
);
} else {
$columnValue = self::texEscape($record[$columns[$i]]);
}
} else { } else {
$columnValue = $this->null; $columnValue = $this->null;
} }

View File

@ -23,6 +23,7 @@ use PhpMyAdmin\Util;
use function __; use function __;
use function array_values; use function array_values;
use function bin2hex;
use function htmlspecialchars; use function htmlspecialchars;
use function str_repeat; use function str_repeat;
@ -226,6 +227,7 @@ class ExportMediawiki extends ExportPlugin
// Get the table data from the database // Get the table data from the database
$result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$fieldsCnt = $result->numFields(); $fieldsCnt = $result->numFields();
$fieldsMeta = $this->dbi->getFieldsMeta($result);
while ($row = $result->fetchRow()) { while ($row = $result->fetchRow()) {
$output .= '|-' . $this->exportCRLF(); $output .= '|-' . $this->exportCRLF();
@ -233,6 +235,12 @@ class ExportMediawiki extends ExportPlugin
// Use '|' for separating table columns // Use '|' for separating table columns
/** @infection-ignore-all */ /** @infection-ignore-all */
for ($i = 0; $i < $fieldsCnt; ++$i) { for ($i = 0; $i < $fieldsCnt; ++$i) {
if (! isset($row[$i])) {
$row[$i] = 'NULL';
} elseif ($fieldsMeta[$i]->isMappedTypeGeometry || $fieldsMeta[$i]->isBinary) {
$row[$i] = '0x' . bin2hex($row[$i]);
}
$output .= ' | ' . $row[$i] . $this->exportCRLF(); $output .= ' | ' . $row[$i] . $this->exportCRLF();
} }
} }

View File

@ -21,6 +21,7 @@ use PhpMyAdmin\Util;
use PhpMyAdmin\Version; use PhpMyAdmin\Version;
use function __; use function __;
use function bin2hex;
use function preg_match; use function preg_match;
use function preg_replace; use function preg_replace;
use function strtr; use function strtr;
@ -127,6 +128,7 @@ class ExportPhparray extends ExportPlugin
$result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$columnsCnt = $result->numFields(); $columnsCnt = $result->numFields();
$fieldsMeta = $this->dbi->getFieldsMeta($result);
$columns = []; $columns = [];
foreach ($result->getFieldNames() as $i => $colAs) { foreach ($result->getFieldNames() as $i => $colAs) {
$colAs = $this->getColumnAlias($aliases, $db, $table, $colAs); $colAs = $this->getColumnAlias($aliases, $db, $table, $colAs);
@ -171,6 +173,10 @@ class ExportPhparray extends ExportPlugin
/** @infection-ignore-all */ /** @infection-ignore-all */
for ($i = 0; $i < $columnsCnt; $i++) { for ($i = 0; $i < $columnsCnt; $i++) {
if ($record[$i] !== null && ($fieldsMeta[$i]->isMappedTypeGeometry || $fieldsMeta[$i]->isBinary)) {
$record[$i] = '0x' . bin2hex($record[$i]);
}
$buffer .= var_export($columns[$i], true) $buffer .= var_export($columns[$i], true)
. ' => ' . var_export($record[$i], true) . ' => ' . var_export($record[$i], true)
. ($i + 1 >= $columnsCnt ? '' : ','); . ($i + 1 >= $columnsCnt ? '' : ',');

View File

@ -25,6 +25,7 @@ use PhpMyAdmin\Triggers\Triggers;
use PhpMyAdmin\Util; use PhpMyAdmin\Util;
use function __; use function __;
use function bin2hex;
use function htmlspecialchars; use function htmlspecialchars;
use function in_array; use function in_array;
use function is_string; use function is_string;
@ -143,6 +144,7 @@ class ExportTexytext extends ExportPlugin
* Gets the data from the database * Gets the data from the database
*/ */
$result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$fieldsMeta = $this->dbi->getFieldsMeta($result);
// If required, get fields name at the first line // If required, get fields name at the first line
if ($this->columns) { if ($this->columns) {
@ -160,11 +162,15 @@ class ExportTexytext extends ExportPlugin
// Format the data // Format the data
while ($row = $result->fetchRow()) { while ($row = $result->fetchRow()) {
$textOutput = ''; $textOutput = '';
foreach ($row as $field) { foreach ($row as $j => $field) {
if ($field === null) { if ($field === null) {
$value = $this->null; $value = $this->null;
} elseif ($field !== '') { } elseif ($field !== '') {
$value = $field; $value = $field;
if ($fieldsMeta[$j]->isMappedTypeGeometry || $fieldsMeta[$j]->isBinary) {
$value = '0x' . bin2hex($value);
}
} else { } else {
$value = ' '; $value = ' ';
} }

View File

@ -25,6 +25,7 @@ use PhpMyAdmin\Util;
use PhpMyAdmin\Version; use PhpMyAdmin\Version;
use function __; use function __;
use function bin2hex;
use function htmlspecialchars; use function htmlspecialchars;
use function mb_substr; use function mb_substr;
use function rtrim; use function rtrim;
@ -402,6 +403,7 @@ class ExportXml extends ExportPlugin
$result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $result = $this->dbi->query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$columnsCnt = $result->numFields(); $columnsCnt = $result->numFields();
$fieldsMeta = $this->dbi->getFieldsMeta($result);
$columns = $result->getFieldNames(); $columns = $result->getFieldNames();
$buffer = ' <!-- ' . __('Table') . ' ' $buffer = ' <!-- ' . __('Table') . ' '
@ -418,6 +420,8 @@ class ExportXml extends ExportPlugin
// the XML structure // the XML structure
if (! isset($record[$i])) { if (! isset($record[$i])) {
$record[$i] = 'NULL'; $record[$i] = 'NULL';
} elseif ($fieldsMeta[$i]->isMappedTypeGeometry || $fieldsMeta[$i]->isBinary) {
$record[$i] = '0x' . bin2hex($record[$i]);
} }
$buffer .= ' <column name="' $buffer .= ' <column name="'

View File

@ -21,6 +21,7 @@ use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function __; use function __;
use function array_key_exists; use function array_key_exists;
use function bin2hex;
use function is_numeric; use function is_numeric;
use function str_replace; use function str_replace;
@ -130,6 +131,10 @@ class ExportYaml extends ExportPlugin
continue; continue;
} }
if ($fieldsMeta[$i]->isMappedTypeGeometry || $fieldsMeta[$i]->isBinary) {
$record[$i] = '0x' . bin2hex($record[$i]);
}
$isNotString = isset($fieldsMeta[$i]) && $fieldsMeta[$i]->isNotType(FieldMetadata::TYPE_STRING); $isNotString = isset($fieldsMeta[$i]) && $fieldsMeta[$i]->isNotType(FieldMetadata::TYPE_STRING);
if (is_numeric($record[$i]) && $isNotString) { if (is_numeric($record[$i]) && $isNotString) {
$buffer .= ' ' . $columns[$i] . ': ' . $record[$i] . "\n"; $buffer .= ' ' . $columns[$i] . ': ' . $record[$i] . "\n";

View File

@ -183,9 +183,10 @@ class Pdf extends PdfLib
/** /**
* Generate table * Generate table
* *
* @param int|float $lineheight Height of line * @param int|float $lineheight Height of line
* @param list<FieldMetadata> $fields meta info for fields in $this->results
*/ */
public function morepagestable(int|float $lineheight = 8): void public function morepagestable(int|float $lineheight, array $fields): void
{ {
// some things to set and 'remember' // some things to set and 'remember'
$l = $this->lMargin; $l = $this->lMargin;
@ -212,6 +213,16 @@ class Pdf extends PdfLib
$this->page = $currpage; $this->page = $currpage;
$this->setXY($l, $h); $this->setXY($l, $h);
if ($this->tablewidths[$col] > 0) { if ($this->tablewidths[$col] > 0) {
if ($txt !== null) {
if ($fields[$col]->isType(FieldMetadata::TYPE_GEOMETRY)) {
$txt = '[GEOMETRY]';
}
if ($fields[$col]->isType(FieldMetadata::TYPE_BLOB) || $fields[$col]->isBinary()) {
$txt = '[BLOB]';
}
}
$this->MultiCell($this->tablewidths[$col], $lineheight, $txt ?? 'NULL', 0, $this->colAlign[$col]); $this->MultiCell($this->tablewidths[$col], $lineheight, $txt ?? 'NULL', 0, $this->colAlign[$col]);
$l += $this->tablewidths[$col]; $l += $this->tablewidths[$col];
} }
@ -690,7 +701,7 @@ class Pdf extends PdfLib
} }
/** /**
* @todo do not deactivate completely the display * do not deactivate completely the display
* but show the field's name and [BLOB] * but show the field's name and [BLOB]
*/ */
if ($fields[$i]->isBinary()) { if ($fields[$i]->isBinary()) {
@ -765,11 +776,12 @@ class Pdf extends PdfLib
// Pass 2 // Pass 2
$this->results = $dbi->query($query, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED); $this->results = $dbi->query($query, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
$fields = $dbi->getFieldsMeta($this->results);
$this->setY($this->tMargin); $this->setY($this->tMargin);
$this->AddPage(); $this->AddPage();
$this->setFont(PdfLib::PMA_PDF_FONT, '', 9); $this->setFont(PdfLib::PMA_PDF_FONT, '', 9);
// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
$this->morepagestable($this->FontSizePt); $this->morepagestable($this->FontSizePt, $fields);
} }
public function setTitleFontSize(int $titleFontSize): void public function setTitleFontSize(int $titleFontSize): void

View File

@ -16,15 +16,13 @@ class ChangePasswordTest extends TestBase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = false; protected bool $createDatabase = false;
/** /**
* Tests the changing of the password * Tests the changing of the password
*/ */
public function testChangePassword(): void public function testChangePassword(): void
{ {
$this->login();
$e = $this->waitForElement('id', 'change_password_anchor'); $e = $this->waitForElement('id', 'change_password_anchor');
$e->click(); $e->click();

View File

@ -20,7 +20,6 @@ class CreateDropDatabaseTest extends TestBase
/* TODO: For now this tests needs superuser for deleting database */ /* TODO: For now this tests needs superuser for deleting database */
$this->skipIfNotSuperUser(); $this->skipIfNotSuperUser();
$this->login();
} }
/** /**

View File

@ -17,7 +17,7 @@ class CreateRemoveUserTest extends TestBase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = false; protected bool $createDatabase = false;
/** /**
* Username for the user * Username for the user
@ -39,7 +39,6 @@ class CreateRemoveUserTest extends TestBase
$this->skipIfNotSuperUser(); $this->skipIfNotSuperUser();
$this->txtUsername = 'test_user_' . bin2hex(random_bytes(4)); $this->txtUsername = 'test_user_' . bin2hex(random_bytes(4));
$this->txtPassword = 'abc_123'; $this->txtPassword = 'abc_123';
$this->login();
} }
/** /**

View File

@ -34,7 +34,7 @@ class EventsTest extends TestBase
. 'INSERT INTO `test_table` (val) VALUES (2);' . 'INSERT INTO `test_table` (val) VALUES (2);'
. 'SET GLOBAL event_scheduler="ON";', . 'SET GLOBAL event_scheduler="ON";',
); );
$this->login();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);
// Let the Database page load // Let the Database page load

View File

@ -12,16 +12,6 @@ use PHPUnit\Framework\Attributes\Large;
#[Large] #[Large]
class OperationsTest extends TestBase class OperationsTest extends TestBase
{ {
/**
* setUp function
*/
protected function setUp(): void
{
parent::setUp();
$this->login();
}
private function getToDBOperations(): void private function getToDBOperations(): void
{ {
$this->gotoHomepage(); $this->gotoHomepage();

View File

@ -49,8 +49,6 @@ class ProceduresTest extends TestBase
. ');', . ');',
); );
$this->login();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);
$this->expandMore(); $this->expandMore();
} }

View File

@ -34,7 +34,6 @@ class StructureTest extends TestBase
. 'INSERT INTO `test_table` (val) VALUES (2);', . 'INSERT INTO `test_table` (val) VALUES (2);',
); );
$this->login();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);
// Let the Database page load // Let the Database page load

View File

@ -30,8 +30,6 @@ class ExportTest extends TestBase
. ');' . ');'
. 'INSERT INTO `test_table` (val) VALUES (2);', . 'INSERT INTO `test_table` (val) VALUES (2);',
); );
$this->login();
} }
/** /**

View File

@ -11,16 +11,6 @@ use PHPUnit\Framework\Attributes\Large;
#[Large] #[Large]
class ImportTest extends TestBase class ImportTest extends TestBase
{ {
/**
* setUp function
*/
protected function setUp(): void
{
parent::setUp();
$this->login();
}
/** /**
* Test for server level import * Test for server level import
*/ */

View File

@ -14,7 +14,12 @@ class LoginTest extends TestBase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = false; protected bool $createDatabase = false;
/**
* Login before starting this test
*/
protected bool $login = false;
protected function setUp(): void protected function setUp(): void
{ {
@ -28,7 +33,6 @@ class LoginTest extends TestBase
*/ */
public function testSuccessfulLogin(): void public function testSuccessfulLogin(): void
{ {
$this->login();
$this->waitForElement('xpath', '//*[@id="server-breadcrumb"]'); $this->waitForElement('xpath', '//*[@id="server-breadcrumb"]');
self::assertTrue($this->isSuccessLogin()); self::assertTrue($this->isSuccessLogin());
$this->logOutIfLoggedIn(); $this->logOutIfLoggedIn();

View File

@ -28,7 +28,6 @@ class NormalizationTest extends TestBase
. ');', . ');',
); );
$this->login();
$this->navigateTable('test_table'); $this->navigateTable('test_table');
$this->waitForElement('xpath', "(//a[contains(., 'Structure')])")->click(); $this->waitForElement('xpath', "(//a[contains(., 'Structure')])")->click();

View File

@ -16,7 +16,7 @@ class ServerSettingsTest extends TestBase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = false; protected bool $createDatabase = false;
/** /**
* setUp function * setUp function
@ -25,7 +25,6 @@ class ServerSettingsTest extends TestBase
{ {
parent::setUp(); parent::setUp();
$this->login();
$this->expandMore(); $this->expandMore();
$this->waitForElement('partialLinkText', 'Settings')->click(); $this->waitForElement('partialLinkText', 'Settings')->click();
$this->waitAjax(); $this->waitAjax();

View File

@ -25,7 +25,6 @@ class SqlQueryTest extends TestBase
. ');' . ');'
. 'INSERT INTO `test_table` (val) VALUES (2), (3), (4), (5);', . 'INSERT INTO `test_table` (val) VALUES (2), (3), (4), (5);',
); );
$this->login();
} }
/** /**

View File

@ -34,7 +34,6 @@ class BrowseTest extends TestBase
. " (3, 'Abcd', '2012-01-20 02:00:02');", . " (3, 'Abcd', '2012-01-20 02:00:02');",
); );
$this->login();
$this->navigateTable('test_table'); $this->navigateTable('test_table');
$this->waitAjax(); $this->waitAjax();

View File

@ -18,7 +18,6 @@ class CreateTest extends TestBase
{ {
parent::setUp(); parent::setUp();
$this->login();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);
} }

View File

@ -30,7 +30,6 @@ class InsertTest extends TestBase
. ');', . ');',
); );
$this->login();
$this->navigateTable('test_table'); $this->navigateTable('test_table');
} }

View File

@ -33,7 +33,6 @@ class OperationsTest extends TestBase
. 'INSERT INTO test_table (val, val2) VALUES (33, 44);', . 'INSERT INTO test_table (val, val2) VALUES (33, 44);',
); );
$this->login();
$this->navigateTable('test_table'); $this->navigateTable('test_table');
$this->waitAjax(); $this->waitAjax();

View File

@ -29,7 +29,6 @@ class StructureTest extends TestBase
. ');', . ');',
); );
$this->login();
$this->navigateTable('test_table'); $this->navigateTable('test_table');
$this->waitForElement('xpath', "(//a[contains(., 'Structure')])")->click(); $this->waitForElement('xpath', "(//a[contains(., 'Structure')])")->click();

View File

@ -84,7 +84,12 @@ abstract class TestBase extends TestCase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = true; protected bool $createDatabase = true;
/**
* Login before starting this test
*/
protected bool $login = true;
/** /**
* Did the test create the phpMyAdmin storage database ? * Did the test create the phpMyAdmin storage database ?
@ -135,8 +140,12 @@ abstract class TestBase extends TestCase
$this->navigateTo(''); $this->navigateTo('');
$this->webDriver->manage()->window()->maximize(); $this->webDriver->manage()->window()->maximize();
if (! static::$createDatabase) { if ($this->login) {
// Stop here, we were not asked to create a database $this->login();
}
if (! $this->createDatabase) {
// Stop here, we where not asked to create a database
return; return;
} }
@ -152,7 +161,7 @@ abstract class TestBase extends TestCase
$this->dbQuery( $this->dbQuery(
'CREATE DATABASE IF NOT EXISTS `' . $this->databaseName . '`; USE `' . $this->databaseName . '`;', 'CREATE DATABASE IF NOT EXISTS `' . $this->databaseName . '`; USE `' . $this->databaseName . '`;',
); );
static::$createDatabase = true; $this->createDatabase = true;
} }
public function getDbPrefix(): string public function getDbPrefix(): string
@ -1085,7 +1094,7 @@ abstract class TestBase extends TestCase
*/ */
protected function tearDown(): void protected function tearDown(): void
{ {
if (static::$createDatabase) { if ($this->createDatabase) {
$this->dbQuery('DROP DATABASE IF EXISTS `' . $this->databaseName . '`;'); $this->dbQuery('DROP DATABASE IF EXISTS `' . $this->databaseName . '`;');
} }

View File

@ -33,7 +33,6 @@ class TrackingTest extends TestBase
. 'INSERT INTO `test_table` (val) VALUES (2), (3);', . 'INSERT INTO `test_table` (val) VALUES (2), (3);',
); );
$this->login();
$this->skipIfNotPMADB(); $this->skipIfNotPMADB();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);

View File

@ -33,8 +33,6 @@ class TriggersTest extends TestBase
. 'INSERT INTO `test_table2` (val) VALUES (2);', . 'INSERT INTO `test_table2` (val) VALUES (2);',
); );
$this->login();
$this->navigateDatabase($this->databaseName); $this->navigateDatabase($this->databaseName);
} }

View File

@ -14,14 +14,7 @@ class XssTest extends TestBase
/** /**
* Create a test database for this test class * Create a test database for this test class
*/ */
protected static bool $createDatabase = false; protected bool $createDatabase = false;
protected function setUp(): void
{
parent::setUp();
$this->login();
}
/** /**
* Tests the SQL query tab with a null query * Tests the SQL query tab with a null query

85
tests/test_data/test.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- phpMyAdmin XML Dump
- version 5.2.4-dev
- https://www.phpmyadmin.net
-
- Host: localhost:3306
- Generation Time: Feb 19, 2026 at 03:28 AM
- Server version: 12.2.2-MariaDB
- PHP Version: 7.2.34
-->
<pma_xml_export version="1.0" xmlns:pma="https://www.phpmyadmin.net/some_doc_url/">
<!--
- Structure schemas
-->
<pma:structure_schemas>
<pma:database name="test" collation="utf8mb4_general_ci" charset="utf8mb4">
<pma:table name="test">
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`binary` binary(16) NOT NULL,
`name` varchar(20) NOT NULL,
`shape` geometry DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
</pma:table>
</pma:database>
</pma:structure_schemas>
<!--
- Database: 'test'
-->
<database name="test">
<!-- Table test -->
<table name="test">
<column name="id">1</column>
<column name="binary">0x10000000000000000000000000000000</column>
<column name="name">POLYGON</column>
<column name="shape">0x00000000010300000001000000040000000000000000405f40000000000000000000000000008063400000000000006940000000000040664000000000004057400000000000405f400000000000000000</column>
</table>
<table name="test">
<column name="id">2</column>
<column name="binary">0x20000000000000000000000000000000</column>
<column name="name">MULTIPOLYGON</column>
<column name="shape">0x00000000010600000002000000010300000001000000040000000000000000006140000000000000444000000000006062400000000000c0544000000000004064400000000000c0524000000000000061400000000000004440010300000001000000040000000000000000405a4000000000000000000000000000004c400000000000006940000000000080534000000000004057400000000000405a400000000000000000</column>
</table>
<table name="test">
<column name="id">3</column>
<column name="binary">0x30000000000000000000000000000000</column>
<column name="name">MULTIPOINT</column>
<column name="shape">0x0000000001040000000400000001010000000000000000405f400000000000004940010100000000000000008063400000000000406f40010100000000000000004066400000000000e0614001010000000000000000e065400000000000005440</column>
</table>
<table name="test">
<column name="id">4</column>
<column name="binary">0x40000000000000000000000000000000</column>
<column name="name">MULTILINESTRING</column>
<column name="shape">0x000000000105000000020000000102000000030000000000000000004240000000000080614000000000008047400000000000206d400000000000004f400000000000c052400102000000030000000000000000004240000000000000594000000000000031400000000000206d4000000000004066400000000000405740</column>
</table>
<table name="test">
<column name="id">5</column>
<column name="binary">0x50000000000000000000000000000000</column>
<column name="name">POINT</column>
<column name="shape">0x00000000010100000000000000000059400000000000406f40</column>
</table>
<table name="test">
<column name="id">6</column>
<column name="binary">0x60000000000000000000000000000000</column>
<column name="name">LINESTRING</column>
<column name="shape">0x0000000001020000000400000000000000008063400000000000003840000000000040664000000000000058400000000000606d4000000000000069400000000000a063400000000000c06140</column>
</table>
<table name="test">
<column name="id">7</column>
<column name="binary">0x70000000000000000000000000000000</column>
<column name="name">GEOMETRYCOLLECTION</column>
<column name="shape">0x000000000107000000030000000101000000000000000000594000000000000059400102000000050000000000000000000000000000000000000000000000000059400000000000005940000000000000694000000000000069400000000000c072400000000000c07240000000000000794000000000000079400103000000020000000500000000000000008041400000000000002440000000000000244000000000000034400000000000002e40000000000000444000000000008046400000000000804640000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40</column>
</table>
<table name="test">
<column name="id">8</column>
<column name="binary">0x80000000000000000000000000000000</column>
<column name="name">TEST</column>
<column name="shape">NULL</column>
</table>
</database>
</pma_xml_export>

View File

@ -108,6 +108,24 @@ final class ImportXmlTest extends AbstractTestCase
pma_bookmarktest (Structure) (Options) pma_bookmarktest (Structure) (Options)
*/ */
//assert that all sql are executed
self::assertSame(
'CREATE DATABASE IF NOT EXISTS `phpmyadmintest` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;'
. 'USE `phpmyadmintest`;' . "\n"
. 'CREATE TABLE IF NOT EXISTS `pma_bookmarktest` (' . "\n"
. ' `id` int(11) NOT NULL AUTO_INCREMENT,' . "\n"
. ' `dbase` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT \'\',' . "\n"
. ' `user` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT \'\',' . "\n"
. ' `label` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT \'\',' . "\n"
. ' `query` text COLLATE utf8_bin NOT NULL,' . "\n"
. ' PRIMARY KEY (`id`)' . "\n"
. ') ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT=\'Bookmarks\';' . "\n"
. ' ;'
. 'INSERT INTO `phpmyadmintest`.`pma_bookmarktest` (`id`, `dbase`, `user`, `label`, `query`) '
. 'VALUES (, \'\', \'\', \'\', \'\');;',
Current::$sqlQuery,
);
self::assertStringContainsString( self::assertStringContainsString(
'The following structures have either been created or altered.', 'The following structures have either been created or altered.',
ImportSettings::$importNotice, ImportSettings::$importNotice,
@ -126,4 +144,50 @@ final class ImportXmlTest extends AbstractTestCase
return new ImportXml(new Import($dbiObject, new ResponseRenderer(), $config), $dbiObject, $config); return new ImportXml(new Import($dbiObject, new ResponseRenderer(), $config), $dbiObject, $config);
} }
/**
* Test for doImport using second dataset
*/
#[RequiresPhpExtension('simplexml')]
public function testDoImportDataset2(): void
{
$dbi = $this->getMockBuilder(DatabaseInterface::class)
->disableOriginalConstructor()
->getMock();
$importHandle = new File(ImportSettings::$importFile);
$importHandle->open();
ImportSettings::$importFile = 'test/test_data/test.xml';
$importXml = $this->getImportXml($dbi);
$importXml->doImport($importHandle);
self::assertSame(
'CREATE DATABASE IF NOT EXISTS `phpmyadmintest` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;'
. 'USE `phpmyadmintest`;' . "\n"
. 'CREATE TABLE IF NOT EXISTS `pma_bookmarktest` (' . "\n"
. ' `id` int(11) NOT NULL AUTO_INCREMENT,' . "\n"
. ' `dbase` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT \'\',' . "\n"
. ' `user` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT \'\',' . "\n"
. ' `label` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT \'\',' . "\n"
. ' `query` text COLLATE utf8_bin NOT NULL,' . "\n"
. ' PRIMARY KEY (`id`)' . "\n"
. ') ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT=\'Bookmarks\';' . "\n"
. ' ;'
. 'INSERT INTO `phpmyadmintest`.`pma_bookmarktest` (`id`, `dbase`, `user`, `label`, `query`) '
. 'VALUES (, \'\', \'\', \'\', \'\');;',
Current::$sqlQuery,
);
self::assertStringContainsString(
'The following structures have either been created or altered.',
ImportSettings::$importNotice,
);
self::assertStringContainsString('Go to database: `test`', ImportSettings::$importNotice);
self::assertStringContainsString('Edit settings for `test`', ImportSettings::$importNotice);
self::assertStringContainsString('Go to table: `test`', ImportSettings::$importNotice);
self::assertStringContainsString('Edit settings for `test`', ImportSettings::$importNotice);
self::assertTrue(ImportSettings::$finished);
}
} }