Skip to content

Commit

Permalink
Merge branch 'QA_5_1'
Browse files Browse the repository at this point in the history
Signed-off-by: William Desportes <williamdes@wdes.fr>
  • Loading branch information
williamdes committed Apr 23, 2021
2 parents 2c22ff1 + 07bf86b commit d82e85f
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 56 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -93,6 +93,10 @@ phpMyAdmin - ChangeLog
- issue Fixed "data is undefined" JS error
- issue Fixed 2 un-translated buttons on central columns edit
- issue #16810 Fixed SQL query shown twice on central columns actions
- issue #16771 Fixed PHP array export to work on very large datasets
- issue #16847 Fixed JSON export nullable binary PHP error
- issue #16847 Fixed JSON export text fields and binary data
- issue #14773 Fix exporting a raw query is not working

5.1.0 (2021-02-24)
- issue #15350 Change Media (MIME) type references to Media type
Expand Down
2 changes: 1 addition & 1 deletion js/src/export_output.js
Expand Up @@ -6,7 +6,7 @@ AJAX.registerOnload('export_output.js', function () {
}
});

$('#export_refresh_btn').on('click', function (e) {
$('.export_refresh_btn').on('click', function (e) {
e.preventDefault();
$('#export_refresh_form').trigger('submit');
});
Expand Down
9 changes: 8 additions & 1 deletion libraries/classes/Controllers/Table/ExportController.php
Expand Up @@ -123,8 +123,14 @@ public function index(): void
return;
}

$exportType = 'table';
$isReturnBackFromRawExport = isset($_POST['export_type']) && $_POST['export_type'] === 'raw';
if (isset($_POST['raw_query']) || $isReturnBackFromRawExport) {
$exportType = 'raw';
}

$options = $this->export->getOptions(
'table',
$exportType,
$db,
$table,
$sql_query,
Expand All @@ -134,6 +140,7 @@ public function index(): void
);

$this->render('table/export/index', array_merge($options, [
'export_type' => $exportType,
'page_settings_error_html' => $pageSettingsErrorHtml,
'page_settings_html' => $pageSettingsHtml,
'message' => $message,
Expand Down
2 changes: 1 addition & 1 deletion libraries/classes/Export.php
Expand Up @@ -623,7 +623,7 @@ public function getHtmlForDisplayedExportHeader(
$html .= $backButton;
$refreshButton = '<form id="export_refresh_form" method="POST" action="'
. Url::getFromRoute('/export') . '" class="disableAjax">';
$refreshButton .= '[ <a class="disableAjax" id="export_refresh_btn">' . __('Refresh') . '</a> ]';
$refreshButton .= '[ <a class="disableAjax export_refresh_btn">' . __('Refresh') . '</a> ]';
foreach ($_POST as $name => $value) {
if (is_array($value)) {
foreach ($value as $val) {
Expand Down
114 changes: 62 additions & 52 deletions libraries/classes/Plugins/Export/ExportJson.php
Expand Up @@ -238,6 +238,42 @@ public function exportData(
'data' => '@@DATA@@',
]
);

return $this->doExportForQuery(
$dbi,
$sqlQuery,
$buffer,
$crlf,
$aliases,
$db,
$table
);
}

/**
* Export to JSON
*
* @phpstan-param array{
* string: array{
* 'tables': array{
* string: array{
* 'columns': array{string: string}
* }
* }
* }
* }|array|null $aliases
*
* @return bool False on export fail and true on export end success
*/
protected function doExportForQuery(
DatabaseInterface $dbi,
string $sqlQuery,
string $buffer,
string $crlf,
?array $aliases,
?string $db,
?string $table
): bool {
[$header, $footer] = explode('"@@DATA@@"', $buffer);

if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) {
Expand All @@ -255,7 +291,10 @@ public function exportData(
$columns = [];
for ($i = 0; $i < $columns_cnt; $i++) {
$col_as = $dbi->fieldName($result, $i);
if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
if (
$db !== null && $table !== null && $aliases !== null
&& ! empty($aliases[$db]['tables'][$table]['columns'][$col_as])
) {
$col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
}

Expand All @@ -276,9 +315,22 @@ public function exportData(
$data = [];

for ($i = 0; $i < $columns_cnt; $i++) {
// 63 is the binary charset, see: https://dev.mysql.com/doc/internals/en/charsets.html
$isBlobAndIsBinaryCharset = isset($fieldsMeta[$i])
&& $fieldsMeta[$i]->isType(FieldMetadata::TYPE_BLOB)
&& $fieldsMeta[$i]->charsetnr === 63;
// This can occur for binary fields
$isBinaryString = isset($fieldsMeta[$i])
&& $fieldsMeta[$i]->isType(FieldMetadata::TYPE_STRING)
&& $fieldsMeta[$i]->charsetnr === 63;
if (
isset($fieldsMeta[$i]) && ($fieldsMeta[$i]->isMappedTypeGeometry
|| $fieldsMeta[$i]->isType(FieldMetadata::TYPE_BLOB))
isset($fieldsMeta[$i]) &&
(
$fieldsMeta[$i]->isMappedTypeGeometry ||
$isBlobAndIsBinaryCharset ||
$isBinaryString
) &&
$record[$i] !== null
) {
// export GIS and blob types as hex
$record[$i] = '0x' . bin2hex($record[$i]);
Expand Down Expand Up @@ -325,57 +377,15 @@ public function exportRawQuery(string $errorUrl, string $sqlQuery, string $crlf)
'data' => '@@DATA@@',
]
);
[$header, $footer] = explode('"@@DATA@@"', $buffer);

if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) {
return false;
}

$result = $dbi->query(
return $this->doExportForQuery(
$dbi,
$sqlQuery,
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_UNBUFFERED
$buffer,
$crlf,
null,
null,
null
);
$columns_cnt = $dbi->numFields($result);

$columns = [];
for ($i = 0; $i < $columns_cnt; $i++) {
$col_as = $dbi->fieldName($result, $i);
$columns[$i] = stripslashes($col_as);
}

$record_cnt = 0;
while ($record = $dbi->fetchRow($result)) {
$record_cnt++;

if ($record_cnt > 1) {
if (! $this->export->outputHandler(',' . $crlf)) {
return false;
}
}

$data = [];

for ($i = 0; $i < $columns_cnt; $i++) {
$data[$columns[$i]] = $record[$i];
}

$encodedData = $this->encode($data);
if (! $encodedData) {
return false;
}

if (! $this->export->outputHandler($encodedData)) {
return false;
}
}

if (! $this->export->outputHandler($crlf . ']' . $crlf . $footer . $crlf)) {
return false;
}

$dbi->freeResult($result);

return true;
}
}
11 changes: 11 additions & 0 deletions libraries/classes/Plugins/Export/ExportPhparray.php
Expand Up @@ -229,7 +229,12 @@ public function exportData(
. $this->commentString(Util::backquote($db_alias)) . '.'
. $this->commentString(Util::backquote($table_alias)) . ' */' . $crlf;
$buffer .= '$' . $tablefixed . ' = array(';
if (! $this->export->outputHandler($buffer)) {
return false;
}

// Reset the buffer
$buffer = '';
while ($record = $dbi->fetchRow($result)) {
$record_cnt++;

Expand All @@ -246,6 +251,12 @@ public function exportData(
}

$buffer .= ')';
if (! $this->export->outputHandler($buffer)) {
return false;
}

// Reset the buffer
$buffer = '';
}

$buffer .= $crlf . ');' . $crlf;
Expand Down
9 changes: 8 additions & 1 deletion templates/table/export/index.twig
Expand Up @@ -2,7 +2,14 @@

{% block message %}{{ message|raw }}{% endblock %}

{% block title %}{{ 'Exporting rows from "%s" table'|trans|format(table) }}{% endblock %}

{% block title %}
{% if export_type == 'raw' %}
{% trans %}Exporting a raw query{% notes %}A query that the user has written freely{% endtrans %}
{% else %}
{{ 'Exporting rows from "%s" table'|trans|format(table) }}
{% endif %}
{% endblock %}

{% set filename_hint %}
{% trans '@SERVER@ will become the server name, @DATABASE@ will become the database name and @TABLE@ will become the table name.' %}
Expand Down
47 changes: 47 additions & 0 deletions test/classes/Plugins/Export/ExportJsonTest.php
Expand Up @@ -199,4 +199,51 @@ public function testExportData(): void
'SELECT * FROM `test_db`.`test_table`;'
));
}

public function testExportComplexData(): void
{
// normalString binaryField textField blobField
$this->expectOutputString(
'{"type":"table","name":"test_table_complex","database":"test_db","data":'
. "\n[\n"
. '{"f1":"\"\'\"><iframe onload=alert(1)>\u0448\u0435\u043b\u043b\u044b",'
. '"f2":"0x3078313233343638353766656665",'
. '"f3":"My awesome\nText","f4":"0x307861663132333466363863353766656665"},' . "\n"
. '{"f1":null,"f2":null,"f3":null,"f4":null},' . "\n"
. '{"f1":"","f2":"0x307831","f3":"\u0448\u0435\u043b\u043b\u044b","f4":"0x307832"}' . "\n"
. "]\n}\n"
);

$this->assertTrue(
$this->object->exportData(
'test_db',
'test_table_complex',
"\n",
'example.com',
'SELECT * FROM `test_db`.`test_table_complex`;'
)
);
}

public function testExportRawComplexData(): void
{
$this->expectOutputString(
'{"type":"raw","data":'
. "\n[\n"
. '{"f1":"\"\'\"><iframe onload=alert(1)>\u0448\u0435\u043b\u043b\u044b",'
. '"f2":"0x3078313233343638353766656665",'
. '"f3":"My awesome\nText","f4":"0x307861663132333466363863353766656665"},' . "\n"
. '{"f1":null,"f2":null,"f3":null,"f4":null},' . "\n"
. '{"f1":"","f2":"0x307831","f3":"\u0448\u0435\u043b\u043b\u044b","f4":"0x307832"}' . "\n"
. "]\n}\n"
);

$this->assertTrue(
$this->object->exportRawQuery(
'example.com',
'SELECT * FROM `test_db`.`test_table_complex`;',
"\n"
)
);
}
}
16 changes: 16 additions & 0 deletions test/classes/Stubs/DbiDummy.php
Expand Up @@ -22,6 +22,7 @@
use function str_replace;
use function trim;

use const MYSQLI_TYPE_BLOB;
use const MYSQLI_TYPE_DATETIME;
use const MYSQLI_TYPE_DECIMAL;
use const MYSQLI_TYPE_STRING;
Expand Down Expand Up @@ -2404,6 +2405,21 @@ private function init(): void
['3', 'Abcd', '2012-01-20 02:00:02'],
],
],
[
'query' => 'SELECT * FROM `test_db`.`test_table_complex`;',
'columns' => ['f1', 'f2', 'f3', 'f4'],
'result' => [
['"\'"><iframe onload=alert(1)>шеллы', '0x12346857fefe', "My awesome\nText", '0xaf1234f68c57fefe'],
[null, null, null, null],
['', '0x1', 'шеллы', '0x2'],
],
'metadata' => [
new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) ['charsetnr' => 33]),
new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) ['charsetnr' => 63]),
new FieldMetadata(MYSQLI_TYPE_BLOB, 0, (object) ['charsetnr' => 23]),
new FieldMetadata(MYSQLI_TYPE_BLOB, 0, (object) ['charsetnr' => 63]),
],
],
[
'query' => 'SHOW PROCEDURE STATUS;',
'columns' => ['Db', 'Name', 'Type'],
Expand Down

0 comments on commit d82e85f

Please sign in to comment.