Visitar URL original
Merge branch 'QA_5_2' · phpmyadmin/phpmyadmin@dd5f087 · GitHub
Skip to content

Commit dd5f087

Browse files
committed
Merge branch 'QA_5_2'
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
2 parents 1e2a287 + d59645d commit dd5f087

12 files changed

Lines changed: 147 additions & 162 deletions

File tree

‎js/src/sql.ts‎

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,10 @@ const onlyOnceElements = [];
195195
* Handles 'Simulate query' button on SQL query box.
196196
*/
197197
const handleSimulateQueryButton = function (): void {
198-
const updateRegExp = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i');
199-
const deleteRegExp = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
200-
let query = '';
198+
const updateRegExp = /^\s*UPDATE\b\s*(((`([^`]|``)+`)|([a-z0-9_$]+))\s*\.\s*)?((`([^`]|``)+`)|([a-z0-9_$]+))\s*\bSET\b/i;
199+
const deleteRegExp = /^\s*DELETE\b\s*((((`([^`]|``)+`)|([a-z0-9_$]+))\s*\.\s*)?((`([^`]|``)+`)|([a-z0-9_$]+))\s*)?\bFROM\b/i;
201200

202-
if (window.codeMirrorEditor) {
203-
query = window.codeMirrorEditor.getValue();
204-
} else {
205-
query = ($('#sqlquery').val() as string);
206-
}
201+
const query = window.codeMirrorEditor ? window.codeMirrorEditor.getValue() : ($('#sqlquery').val() as string);
207202

208203
const $simulateDml = $('#simulate_dml');
209204
if (updateRegExp.test(query) || deleteRegExp.test(query)) {

‎phpstan-baseline.neon‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5021,7 +5021,7 @@ parameters:
50215021
path: src/Controllers/Sql/SetValuesController.php
50225022

50235023
-
5024-
message: "#^Parameter \\#1 \\$string of function htmlentities expects string, mixed given\\.$#"
5024+
message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#"
50255025
count: 1
50265026
path: src/Controllers/Sql/SetValuesController.php
50275027

@@ -13066,12 +13066,12 @@ parameters:
1306613066
path: src/ParseAnalyze.php
1306713067

1306813068
-
13069-
message: "#^Offset 'select_tables' on array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\}&array\\{select_expr\\?\\: array\\<string\\|null\\>, select_tables\\?\\: array\\<array\\{string, string\\|null\\}\\>, statement\\?\\: PhpMyAdmin\\\\SqlParser\\\\Statement\\|null, parser\\?\\: PhpMyAdmin\\\\SqlParser\\\\Parser\\} in empty\\(\\) does not exist\\.$#"
13069+
message: "#^Offset 'select_tables' on array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\} in empty\\(\\) does not exist\\.$#"
1307013070
count: 1
1307113071
path: src/ParseAnalyze.php
1307213072

1307313073
-
13074-
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\}&array\\{select_expr\\?\\: array\\<string\\|null\\>, select_tables\\?\\: array\\<array\\{string, string\\|null\\}\\>, statement\\?\\: PhpMyAdmin\\\\SqlParser\\\\Statement\\|null, parser\\?\\: PhpMyAdmin\\\\SqlParser\\\\Parser\\} given\\.$#"
13074+
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\} given\\.$#"
1307513075
count: 1
1307613076
path: src/ParseAnalyze.php
1307713077

@@ -17911,7 +17911,7 @@ parameters:
1791117911
path: src/Sql.php
1791217912

1791317913
-
17914-
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\}&array\\{select_expr\\?\\: array\\<string\\|null\\>, select_tables\\?\\: array\\<array\\{string, string\\|null\\}\\>, statement\\?\\: PhpMyAdmin\\\\SqlParser\\\\Statement\\|null, parser\\?\\: PhpMyAdmin\\\\SqlParser\\\\Parser\\} given\\.$#"
17914+
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\} given\\.$#"
1791517915
count: 1
1791617916
path: src/Sql.php
1791717917

@@ -20486,7 +20486,7 @@ parameters:
2048620486
path: test/classes/Display/ResultsTest.php
2048720487

2048820488
-
20489-
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\}&array\\{select_expr\\?\\: array\\<string\\|null\\>, select_tables\\?\\: array\\<array\\{string, string\\|null\\}\\>, statement\\?\\: PhpMyAdmin\\\\SqlParser\\\\Statement\\|null, parser\\?\\: PhpMyAdmin\\\\SqlParser\\\\Parser\\} given\\.$#"
20489+
message: "#^Parameter \\#1 \\$info of static method PhpMyAdmin\\\\StatementInfo\\:\\:fromArray\\(\\) expects array\\{distinct\\: bool, drop_database\\: bool, group\\: bool, having\\: bool, is_affected\\: bool, is_analyse\\: bool, is_count\\: bool, is_delete\\: bool, \\.\\.\\.\\}, array\\{distinct\\?\\: bool, drop_database\\?\\: bool, group\\?\\: bool, having\\?\\: bool, is_affected\\?\\: bool, is_analyse\\?\\: bool, is_count\\?\\: bool, is_delete\\?\\: bool, \\.\\.\\.\\} given\\.$#"
2049020490
count: 3
2049120491
path: test/classes/Display/ResultsTest.php
2049220492

‎src/Controllers/Sql/EnumValuesController.php‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@
1212
use PhpMyAdmin\Template;
1313

1414
use function __;
15-
use function htmlentities;
1615
use function strval;
1716

18-
use const ENT_COMPAT;
19-
2017
final class EnumValuesController extends AbstractController
2118
{
2219
public function __construct(
@@ -46,12 +43,9 @@ public function __invoke(ServerRequest $request): void
4643
return;
4744
}
4845

49-
// Converts characters of $curr_value to HTML entities.
50-
$convertedCurrentValue = htmlentities(strval($currValue), ENT_COMPAT, 'UTF-8');
51-
5246
$dropdown = $this->template->render('sql/enum_column_dropdown', [
5347
'values' => $values,
54-
'selected_values' => [$convertedCurrentValue],
48+
'selected_values' => [strval($currValue)],
5549
]);
5650

5751
$this->response->addJSON('dropdown', $dropdown);

‎src/Controllers/Sql/SetValuesController.php‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
use PhpMyAdmin\Template;
1313

1414
use function __;
15-
use function htmlentities;
16-
17-
use const ENT_COMPAT;
15+
use function explode;
1816

1917
final class SetValuesController extends AbstractController
2018
{
@@ -57,12 +55,9 @@ public function __invoke(ServerRequest $request): void
5755
);
5856
}
5957

60-
// Converts characters of $currentValue to HTML entities.
61-
$convertedCurrentValue = htmlentities($currentValue, ENT_COMPAT, 'UTF-8');
62-
6358
$select = $this->template->render('sql/set_column', [
6459
'values' => $values,
65-
'current_values' => $convertedCurrentValue,
60+
'current_values' => explode(',', $currentValue),
6661
]);
6762

6863
$this->response->addJSON('select', $select);

‎src/Import/SimulateDml.php‎

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PhpMyAdmin\DatabaseInterface;
99
use PhpMyAdmin\Html;
1010
use PhpMyAdmin\SqlParser\Parser;
11-
use PhpMyAdmin\SqlParser\Statement;
1211
use PhpMyAdmin\SqlParser\Statements\DeleteStatement;
1312
use PhpMyAdmin\SqlParser\Statements\UpdateStatement;
1413
use PhpMyAdmin\SqlParser\Utils\Query;
@@ -37,28 +36,27 @@ public function getError(): string
3736
* @return array<string, int|string>
3837
* @psalm-return array{
3938
* sql_query: string,
40-
* matched_rows: (int|numeric-string),
39+
* matched_rows: int,
4140
* matched_rows_url: string
4241
* }
4342
*/
4443
public function getMatchedRows(
4544
string $query,
4645
Parser $parser,
47-
DeleteStatement|UpdateStatement|Statement $statement,
46+
DeleteStatement|UpdateStatement $statement,
4847
): array {
49-
$matchedRowQuery = '';
5048
if ($statement instanceof DeleteStatement) {
51-
$matchedRowQuery = $this->getSimulatedDeleteQuery($parser, $statement);
52-
} elseif ($statement instanceof UpdateStatement) {
53-
$matchedRowQuery = $this->getSimulatedUpdateQuery($parser, $statement);
49+
$matchedRowsQuery = $this->getSimulatedDeleteQuery($parser, $statement);
50+
} else {
51+
$matchedRowsQuery = $this->getSimulatedUpdateQuery($parser, $statement);
5452
}
5553

5654
// Execute the query and get the number of matched rows.
57-
$matchedRows = $this->executeMatchedRowQuery($matchedRowQuery);
55+
$matchedRows = $this->executeMatchedRowQuery($matchedRowsQuery);
5856
$matchedRowsUrl = Url::getFromRoute('/sql', [
5957
'db' => $GLOBALS['db'],
60-
'sql_query' => $matchedRowQuery,
61-
'sql_signature' => Core::signSqlQuery($matchedRowQuery),
58+
'sql_query' => $matchedRowsQuery,
59+
'sql_signature' => Core::signSqlQuery($matchedRowsQuery),
6260
]);
6361

6462
return [
@@ -76,9 +74,12 @@ public function getMatchedRows(
7674
private function executeMatchedRowQuery(string $matchedRowQuery): int
7775
{
7876
$this->dbi->selectDb($GLOBALS['db']);
77+
$result = $this->dbi->tryQuery($matchedRowQuery);
78+
if ($result === false) {
79+
return 0;
80+
}
7981

80-
// Execute the query.
81-
return (int) $this->dbi->fetchValue($matchedRowQuery);
82+
return (int) $result->numRows();
8283
}
8384

8485
/**
@@ -99,8 +100,7 @@ private function getSimulatedDeleteQuery(Parser $parser, DeleteStatement $statem
99100
: ' ORDER BY ' . Query::getClause($statement, $parser->list, 'ORDER BY');
100101
$limit = $statement->limit === null ? '' : ' LIMIT ' . Query::getClause($statement, $parser->list, 'LIMIT');
101102

102-
return 'SELECT COUNT(*) FROM (' .
103-
'SELECT 1 FROM ' . $tableReferences[0] . $where . $order . $limit . ') AS `pma_tmp`';
103+
return 'SELECT * FROM ' . $tableReferences[0] . $where . $order . $limit;
104104
}
105105

106106
/**
@@ -114,21 +114,18 @@ private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statem
114114
Assert::count($tableReferences, 1, 'No joins allowed in simulation query');
115115
Assert::isNonEmptyList($statement->set, 'SET statements missing');
116116
Assert::notNull($parser->list, 'Parser list not set');
117-
$newValues = [];
118-
$oldValues = [];
117+
118+
$values = [];
119119
$newColumns = [];
120120
$oldColumns = [];
121-
$i = 0;
122-
$handledColumns = [];
123121
foreach (array_reverse($statement->set) as $set) {
124-
if (array_key_exists($set->column, $handledColumns)) {
122+
$column = Util::unQuote($set->column);
123+
if (array_key_exists($column, $values)) {
125124
continue;
126125
}
127126

128-
$handledColumns[$set->column] = true;
129-
$oldValues[] = $set->column . ' AS ' . ($oldColumns[] = Util::backquote('o' . $i));
130-
$newValues[] = $set->value . ' AS ' . ($newColumns[] = Util::backquote('n' . $i));
131-
++$i;
127+
$oldColumns[] = Util::backquote($column);
128+
$values[$column] = $set->value . ' AS ' . ($newColumns[] = Util::backquote($column . ' `new`'));
132129
}
133130

134131
$condition = Query::getClause($statement, $parser->list, 'WHERE');
@@ -138,11 +135,10 @@ private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statem
138135
: ' ORDER BY ' . Query::getClause($statement, $parser->list, 'ORDER BY');
139136
$limit = $statement->limit === null ? '' : ' LIMIT ' . Query::getClause($statement, $parser->list, 'LIMIT');
140137

141-
return 'SELECT COUNT(*)' .
142-
' FROM (SELECT ' . implode(',', $newValues) . ') AS `pma_new`' .
143-
' JOIN (' .
144-
'SELECT ' . implode(', ', $oldValues) . ' FROM ' . $tableReferences[0] . $where . $order . $limit .
145-
') AS `pma_old`' .
146-
' WHERE NOT (' . implode(', ', $newColumns) . ') <=> (' . implode(', ', $oldColumns) . ')';
138+
return 'SELECT *' .
139+
' FROM (' .
140+
'SELECT *, ' . implode(', ', $values) . ' FROM ' . $tableReferences[0] . $where . $order . $limit .
141+
') AS `pma_tmp`' .
142+
' WHERE NOT (' . implode(', ', $oldColumns) . ') <=> (' . implode(', ', $newColumns) . ')';
147143
}
148144
}

‎src/Sql.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ public function getValuesForColumn(string $db, string $table, string $columnName
328328
return null;
329329
}
330330

331-
return Util::parseEnumSetValues($column->type);
331+
return Util::parseEnumSetValues($column->type, false);
332332
}
333333

334334
/**

‎src/Util.php‎

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
use function floor;
3535
use function fread;
3636
use function function_exists;
37-
use function html_entity_decode;
3837
use function htmlentities;
3938
use function htmlspecialchars;
4039
use function htmlspecialchars_decode;
@@ -77,6 +76,7 @@
7776
use function strrev;
7877
use function strtolower;
7978
use function strtr;
79+
use function substr;
8080
use function time;
8181
use function trim;
8282
use function uksort;
@@ -1634,47 +1634,16 @@ public static function getServerType(): string
16341634
*/
16351635
public static function parseEnumSetValues(string $definition, bool $escapeHtml = true): array
16361636
{
1637-
$valuesString = htmlentities($definition, ENT_COMPAT, 'UTF-8');
16381637
// There is a JS port of the below parser in functions.js
16391638
// If you are fixing something here,
16401639
// you need to also update the JS port.
1641-
$values = [];
1642-
$inString = false;
1643-
$buffer = '';
1644-
1645-
/** @infection-ignore-all */
1646-
for ($i = 0, $length = mb_strlen($valuesString); $i < $length; $i++) {
1647-
$curr = mb_substr($valuesString, $i, 1);
1648-
$next = $i == mb_strlen($valuesString) - 1
1649-
? ''
1650-
: mb_substr($valuesString, $i + 1, 1);
1651-
1652-
if (! $inString && $curr == "'") {
1653-
$inString = true;
1654-
} elseif (($inString && $curr === '\\') && $next === '\\') {
1655-
$buffer .= '&#92;';
1656-
$i++;
1657-
} elseif (($inString && $next == "'") && ($curr == "'" || $curr === '\\')) {
1658-
$buffer .= '&#39;';
1659-
$i++;
1660-
} elseif ($inString && $curr == "'") {
1661-
$inString = false;
1662-
$values[] = $buffer;
1663-
$buffer = '';
1664-
} elseif ($inString) {
1665-
$buffer .= $curr;
1666-
}
1667-
}
16681640

1669-
if (strlen($buffer) > 0) {
1670-
// The leftovers in the buffer are the last value (if any)
1671-
$values[] = $buffer;
1672-
}
1641+
preg_match('|\((.*)\)|', $definition, $matches);
16731642

1674-
if (! $escapeHtml) {
1675-
foreach ($values as $key => $value) {
1676-
$values[$key] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
1677-
}
1643+
$values = [];
1644+
foreach (explode(',', $matches[1]) as $value) {
1645+
$value = strtr(substr($value, 1, -1), ["''" => "'", "\\'" => "'", '\\\\' => '\\']);
1646+
$values[] = $escapeHtml ? htmlspecialchars($value, ENT_QUOTES, 'UTF-8') : $value;
16781647
}
16791648

16801649
return $values;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<select>
22
{% for value in values %}
3-
<option value="{{ value|raw }}"{{ value in selected_values ? " selected" }}>{{ value|raw }}</option>
3+
<option value="{{ value }}"{{ value in selected_values ? " selected" }}>{{ value }}</option>
44
{% endfor %}
55
</select>

‎templates/sql/set_column.twig‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{% set values_amount = values|length %}
2-
{% set selected_values = current_values|split(',') %}
32
<select class="resize-vertical" size="{{ values_amount < 10 ? values_amount : 10 }}" multiple>
43
{% for value in values %}
5-
<option value="{{ value|raw }}"{{ value in selected_values ? ' selected' }}>{{ value|raw }}</option>
4+
<option value="{{ value }}"{{ value in current_values ? ' selected' }}>{{ value }}</option>
65
{% endfor %}
76
</select>

‎test/classes/Controllers/Sql/EnumValuesControllerTest.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function testGetEnumValuesSuccess(): void
7373
[
7474
[
7575
'set',
76-
'set(\'<script>alert("ok")</script>\',\'a&b\',\'b&c\',\'vrai&amp\',\'\')',
76+
"set('<script>alert(\"ok\")</script>','a&b','b&c','vrai&amp','','漢字','''','\\','\\\"\\\\''')",
7777
'No',
7878
'',
7979
'NULL',
@@ -112,6 +112,10 @@ public function testGetEnumValuesSuccess(): void
112112
. ' <option value="b&amp;c" selected>b&amp;c</option>' . "\n"
113113
. ' <option value="vrai&amp;amp">vrai&amp;amp</option>' . "\n"
114114
. ' <option value=""></option>' . "\n"
115+
. ' <option value="漢字">漢字</option>' . "\n"
116+
. ' <option value="&#039;">&#039;</option>' . "\n"
117+
. ' <option value="\">\</option>' . "\n"
118+
. ' <option value="\&quot;\&#039;">\&quot;\&#039;</option>' . "\n"
115119
. ' </select>' . "\n",
116120
],
117121
$this->getResponseJsonResult(),

0 commit comments

Comments
 (0)