Skip to content

Commit 827f371

Browse files
committed
Reduce memory consumption of simulate query feature
Use a separate query to count changed rows. Wrap select query with limit with a select * query which enables paging for the displayed rows. Signed-off-by: Maximilian Krög <maxi_kroeg@web.de>
1 parent bf68e1e commit 827f371

3 files changed

Lines changed: 121 additions & 116 deletions

File tree

libraries/classes/Import/SimulateDml.php

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ public function getMatchedRows(Parser $parser, Statement $statement): array
5656
}
5757

5858
// Execute the query and get the number of matched rows.
59-
$matchedRows = $this->executeMatchedRowQuery($matchedRowsQuery);
59+
$matchedRows = $this->executeMatchedRowQuery($matchedRowsQuery['count']);
60+
$selectQuery = $matchedRowsQuery['select'];
6061
$matchedRowsUrl = Url::getFromRoute('/sql', [
6162
'db' => $GLOBALS['db'],
62-
'sql_query' => $matchedRowsQuery,
63-
'sql_signature' => Core::signSqlQuery($matchedRowsQuery),
63+
'sql_query' => $selectQuery,
64+
'sql_signature' => Core::signSqlQuery($selectQuery),
6465
]);
6566

6667
return [
@@ -78,20 +79,20 @@ public function getMatchedRows(Parser $parser, Statement $statement): array
7879
private function executeMatchedRowQuery(string $matchedRowQuery): int
7980
{
8081
$this->dbi->selectDb($GLOBALS['db']);
81-
$result = $this->dbi->tryQuery($matchedRowQuery);
82-
if (! $result) {
82+
$count = $this->dbi->fetchValue($matchedRowQuery);
83+
if ($count === false) {
8384
return 0;
8485
}
8586

86-
return (int) $result->numRows();
87+
return (int) $count;
8788
}
8889

8990
/**
9091
* Transforms a DELETE query into SELECT statement.
9192
*
92-
* @return string SQL query
93+
* @psalm-return array{select: string, count: string} SQL queries
9394
*/
94-
private function getSimulatedDeleteQuery(Parser $parser, DeleteStatement $statement): string
95+
private function getSimulatedDeleteQuery(Parser $parser, DeleteStatement $statement): array
9596
{
9697
$tableReferences = Query::getTables($statement);
9798
Assert::count($tableReferences, 1, 'No joins allowed in simulation query');
@@ -104,15 +105,22 @@ private function getSimulatedDeleteQuery(Parser $parser, DeleteStatement $statem
104105
: ' ORDER BY ' . Query::getClause($statement, $parser->list, 'ORDER BY');
105106
$limit = $statement->limit === null ? '' : ' LIMIT ' . Query::getClause($statement, $parser->list, 'LIMIT');
106107

107-
return 'SELECT * FROM ' . $tableReferences[0] . $where . $order . $limit;
108+
return [
109+
'select' => 'SELECT * FROM (' .
110+
'SELECT * FROM ' . $tableReferences[0] . $where . $order . $limit .
111+
') AS pma_tmp',
112+
'count' => 'SELECT COUNT(*) FROM (' .
113+
'SELECT 1 FROM ' . $tableReferences[0] . $where . $order . $limit .
114+
') AS pma_tmp',
115+
];
108116
}
109117

110118
/**
111119
* Transforms a UPDATE query into SELECT statement.
112120
*
113-
* @return string SQL query
121+
* @psalm-return array{select: string, count: string} SQL queies
114122
*/
115-
private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statement): string
123+
private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statement): array
116124
{
117125
$tableReferences = Query::getTables($statement);
118126
Assert::count($tableReferences, 1, 'No joins allowed in simulation query');
@@ -129,7 +137,7 @@ private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statem
129137
}
130138

131139
$oldColumns[] = Util::backquote($column);
132-
$values[$column] = $set->value . ' AS ' . ($newColumns[] = Util::backquote($column . ' `new`'));
140+
$values[$column] = $set->value . ' AS ' . ($newColumns[] = Util::backquote($column . ' *new*'));
133141
}
134142

135143
$condition = Query::getClause($statement, $parser->list, 'WHERE');
@@ -139,10 +147,18 @@ private function getSimulatedUpdateQuery(Parser $parser, UpdateStatement $statem
139147
: ' ORDER BY ' . Query::getClause($statement, $parser->list, 'ORDER BY');
140148
$limit = $statement->limit === null ? '' : ' LIMIT ' . Query::getClause($statement, $parser->list, 'LIMIT');
141149

142-
return 'SELECT *' .
143-
' FROM (' .
144-
'SELECT *, ' . implode(', ', $values) . ' FROM ' . $tableReferences[0] . $where . $order . $limit .
145-
') AS `pma_tmp`' .
146-
' WHERE NOT (' . implode(', ', $oldColumns) . ') <=> (' . implode(', ', $newColumns) . ')';
150+
return [
151+
'select' => 'SELECT *' .
152+
' FROM (' .
153+
'SELECT *, ' . implode(', ', $values) . ' FROM ' . $tableReferences[0] . $where . $order . $limit .
154+
') AS `pma_tmp`' .
155+
' WHERE NOT (' . implode(', ', $oldColumns) . ') <=> (' . implode(', ', $newColumns) . ')',
156+
'count' => 'SELECT COUNT(*)' .
157+
' FROM (' .
158+
'SELECT ' . implode(', ', $oldColumns) . ', ' . implode(', ', $values) .
159+
' FROM ' . $tableReferences[0] . $where . $order . $limit .
160+
') AS `pma_tmp`' .
161+
' WHERE NOT (' . implode(', ', $oldColumns) . ') <=> (' . implode(', ', $newColumns) . ')',
162+
];
147163
}
148164
}

phpstan-baseline.neon

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20755,11 +20755,6 @@ parameters:
2075520755
count: 1
2075620756
path: libraries/classes/Import/Ajax.php
2075720757

20758-
-
20759-
message: "#^Only booleans are allowed in a negated boolean, PhpMyAdmin\\\\Dbal\\\\ResultInterface\\|false given\\.$#"
20760-
count: 1
20761-
path: libraries/classes/Import/SimulateDml.php
20762-
2076320758
-
2076420759
message: "#^Parameter \\#1 \\$dbname of method PhpMyAdmin\\\\DatabaseInterface\\:\\:selectDb\\(\\) expects PhpMyAdmin\\\\Dbal\\\\DatabaseName\\|string, mixed given\\.$#"
2076520760
count: 1

0 commit comments

Comments
 (0)