Ref #16847 - Fix JSON export code duplicated and not following rules in raw query export mode

Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2021-04-23 17:48:14 +02:00
parent 45aeee579a
commit cbd35c9fa8
No known key found for this signature in database
GPG Key ID: 90A0EF1B8251A889
2 changed files with 145 additions and 49 deletions

View File

@ -231,6 +231,42 @@ class ExportJson extends ExportPlugin
'data' => '@@DATA@@',
]
);
return $this->doExportForQuery(
$dbi,
$sql_query,
$buffer,
$crlf,
$aliases,
$db,
$table
);
}
/**
* Export to JSON
*
* @return bool False on export fail and true on export end success
*
* @phpstan-param array{
* string: array{
* 'tables': array{
* string: array{
* 'columns': array{string: string}
* }
* }
* }
* }|array|null $aliases
*/
protected function doExportForQuery(
DatabaseInterface $dbi,
string $sql_query,
string $buffer,
string $crlf,
?array $aliases,
?string $db,
?string $table
): bool {
[$header, $footer] = explode('"@@DATA@@"', $buffer);
if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) {
@ -248,7 +284,9 @@ class ExportJson extends ExportPlugin
$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];
}
$columns[$i] = stripslashes($col_as);
@ -323,56 +361,15 @@ class ExportJson extends ExportPlugin
'data' => '@@DATA@@',
]
);
[$header, $footer] = explode('"@@DATA@@"', $buffer);
if (! $this->export->outputHandler($header . $crlf . '[' . $crlf)) {
return false;
}
$result = $dbi->query(
return $this->doExportForQuery(
$dbi,
$sql_query,
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;
}
}

View File

@ -340,4 +340,103 @@ class ExportJsonTest extends AbstractTestCase
$this->object->exportData('db', 'tbl', "\n", 'example.com', 'SELECT')
);
}
public function testExportRawComplexData(): void
{
$dbi = $this->getMockBuilder(DatabaseInterface::class)
->disableOriginalConstructor()
->getMock();
$flags = [];
$normalString = new stdClass();
$normalString->blob = false;
$normalString->numeric = false;
$normalString->type = 'string';
$normalString->name = 'f1';
$normalString->charsetnr = 33;
$normalString->length = 20;
$flags[] = $normalString;
$binaryField = new stdClass();
$binaryField->blob = false;
$binaryField->numeric = false;
$binaryField->type = 'string';
$binaryField->name = 'f1';
$binaryField->charsetnr = 63;
$binaryField->length = 20;
$flags[] = $binaryField;
$textField = new stdClass();
$textField->blob = false;
$textField->numeric = false;
$textField->type = 'blob';
$textField->name = 'f1';
$textField->charsetnr = 23;
$textField->length = 20;
$flags[] = $textField;
$blobField = new stdClass();
$blobField->blob = false;
$blobField->numeric = false;
$blobField->type = 'blob';
$blobField->name = 'f1';
$blobField->charsetnr = 63;
$blobField->length = 20;
$flags[] = $blobField;
$dbi->expects($this->once())
->method('getFieldsMeta')
->with(null)
->will($this->returnValue($flags));
$dbi->expects($this->once())
->method('numFields')
->with(null)
->will($this->returnValue(4));
$dbi->expects($this->exactly(4))
->method('fieldName')
->withConsecutive(
[null, 0],
[null, 1],
[null, 2],
[null, 3]
)
->willReturnOnConsecutiveCalls(
'f1',
'f2',
'f3',
'f4'
);
$dbi->expects($this->exactly(4))
->method('fetchRow')
->withConsecutive(
[null],
[null],
[null],
[null]
)
->willReturnOnConsecutiveCalls(
// normalString binaryField textField blobField
['"\'"><iframe onload=alert(1)>шеллы', '0x12346857fefe', "My awesome\nText", '0xaf1234f68c57fefe'],
[null, null, null, null],
['', '0x1', 'шеллы', '0x2'],
null// No more data
);
$GLOBALS['dbi'] = $dbi;
$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', "\n")
);
}
}