Skip to content

Commit 58ed5bf

Browse files
committed
Extract Simulate DML action from ImportController
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
1 parent 0f678e8 commit 58ed5bf

12 files changed

Lines changed: 359 additions & 369 deletions

File tree

js/src/sql.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,12 +744,11 @@ AJAX.registerOnload('sql.js', function () {
744744
var $msgbox = Functions.ajaxShowMessage();
745745
$.ajax({
746746
type: 'POST',
747-
url: $form.attr('action'),
747+
url: 'index.php?route=/import/simulate-dml',
748748
data: {
749749
'server': CommonParams.get('server'),
750750
'db': dbName,
751751
'ajax_request': '1',
752-
'simulate_dml': '1',
753752
'sql_query': query,
754753
'sql_delimiter': delimiter
755754
},

libraries/classes/Controllers/Import/ImportController.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,6 @@ public function __invoke(): void
9292
$local_import_file = $_POST['local_import_file'] ?? null;
9393
$show_as_php = $_POST['show_as_php'] ?? null;
9494

95-
// If there is a request to 'Simulate DML'.
96-
if (isset($_POST['simulate_dml'])) {
97-
$this->import->handleSimulateDmlRequest();
98-
99-
return;
100-
}
101-
10295
// If it's a refresh console bookmarks request
10396
if (isset($_GET['console_bookmark_refresh'])) {
10497
$this->response->addJSON(
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpMyAdmin\Controllers\Import;
6+
7+
use PhpMyAdmin\Controllers\AbstractController;
8+
use PhpMyAdmin\Import\SimulateDml;
9+
use PhpMyAdmin\Message;
10+
use PhpMyAdmin\ResponseRenderer;
11+
use PhpMyAdmin\SqlParser\Parser;
12+
use PhpMyAdmin\SqlParser\Statements\DeleteStatement;
13+
use PhpMyAdmin\SqlParser\Statements\UpdateStatement;
14+
use PhpMyAdmin\SqlParser\Utils\Query;
15+
use PhpMyAdmin\Template;
16+
17+
use function __;
18+
use function count;
19+
use function explode;
20+
21+
final class SimulateDmlController extends AbstractController
22+
{
23+
/** @var SimulateDml */
24+
private $simulateDml;
25+
26+
public function __construct(
27+
ResponseRenderer $response,
28+
Template $template,
29+
SimulateDml $simulateDml
30+
) {
31+
parent::__construct($response, $template);
32+
$this->simulateDml = $simulateDml;
33+
}
34+
35+
public function __invoke(): void
36+
{
37+
$error = '';
38+
$errorMsg = __('Only single-table UPDATE and DELETE queries can be simulated.');
39+
/** @var string $sqlDelimiter */
40+
$sqlDelimiter = $_POST['sql_delimiter'];
41+
$sqlData = [];
42+
/** @var string[] $queries */
43+
$queries = explode($sqlDelimiter, $GLOBALS['sql_query']);
44+
foreach ($queries as $sqlQuery) {
45+
if (empty($sqlQuery)) {
46+
continue;
47+
}
48+
49+
// Parsing the query.
50+
$parser = new Parser($sqlQuery);
51+
52+
if (empty($parser->statements[0])) {
53+
continue;
54+
}
55+
56+
$statement = $parser->statements[0];
57+
58+
if (
59+
! ($statement instanceof UpdateStatement || $statement instanceof DeleteStatement)
60+
|| ! empty($statement->join)
61+
) {
62+
$error = $errorMsg;
63+
break;
64+
}
65+
66+
$tables = Query::getTables($statement);
67+
if (count($tables) > 1) {
68+
$error = $errorMsg;
69+
break;
70+
}
71+
72+
// Get the matched rows for the query.
73+
$result = $this->simulateDml->getMatchedRows($sqlQuery, $parser, $statement);
74+
$error = $this->simulateDml->getError();
75+
76+
if ($error !== '') {
77+
break;
78+
}
79+
80+
$sqlData[] = $result;
81+
}
82+
83+
if ($error) {
84+
$message = Message::rawError($error);
85+
$this->response->addJSON('message', $message);
86+
$this->response->addJSON('sql_data', false);
87+
88+
return;
89+
}
90+
91+
$this->response->addJSON('sql_data', $sqlData);
92+
}
93+
}

libraries/classes/Import.php

Lines changed: 0 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
use function strcmp;
3838
use function strlen;
3939
use function strpos;
40-
use function strtoupper;
4140
use function substr;
4241
use function time;
4342
use function trim;
@@ -1342,225 +1341,6 @@ public function buildSql(
13421341
$import_notice = $message;
13431342
}
13441343

1345-
/**
1346-
* Handles request for Simulation of UPDATE/DELETE queries.
1347-
*/
1348-
public function handleSimulateDmlRequest(): void
1349-
{
1350-
global $dbi;
1351-
1352-
$response = ResponseRenderer::getInstance();
1353-
$error = '';
1354-
$errorMsg = __('Only single-table UPDATE and DELETE queries can be simulated.');
1355-
$sqlDelimiter = $_POST['sql_delimiter'];
1356-
$sqlData = [];
1357-
$queries = explode($sqlDelimiter, $GLOBALS['sql_query']);
1358-
foreach ($queries as $sqlQuery) {
1359-
if (empty($sqlQuery)) {
1360-
continue;
1361-
}
1362-
1363-
// Parsing the query.
1364-
$parser = new Parser($sqlQuery);
1365-
1366-
if (empty($parser->statements[0])) {
1367-
continue;
1368-
}
1369-
1370-
$statement = $parser->statements[0];
1371-
1372-
$analyzedSqlResults = [
1373-
'query' => $sqlQuery,
1374-
'parser' => $parser,
1375-
'statement' => $statement,
1376-
];
1377-
1378-
if (
1379-
! ($statement instanceof UpdateStatement
1380-
|| $statement instanceof DeleteStatement)
1381-
|| ! empty($statement->join)
1382-
) {
1383-
$error = $errorMsg;
1384-
break;
1385-
}
1386-
1387-
$tables = Query::getTables($statement);
1388-
if (count($tables) > 1) {
1389-
$error = $errorMsg;
1390-
break;
1391-
}
1392-
1393-
// Get the matched rows for the query.
1394-
$result = $this->getMatchedRows($analyzedSqlResults);
1395-
$error = $dbi->getError();
1396-
1397-
if ($error !== '') {
1398-
break;
1399-
}
1400-
1401-
$sqlData[] = $result;
1402-
}
1403-
1404-
if ($error) {
1405-
$message = Message::rawError($error);
1406-
$response->addJSON('message', $message);
1407-
$response->addJSON('sql_data', false);
1408-
} else {
1409-
$response->addJSON('sql_data', $sqlData);
1410-
}
1411-
}
1412-
1413-
/**
1414-
* Find the matching rows for UPDATE/DELETE query.
1415-
*
1416-
* @param array $analyzedSqlResults Analyzed SQL results from parser.
1417-
*
1418-
* @return array
1419-
*/
1420-
public function getMatchedRows(array $analyzedSqlResults = []): array
1421-
{
1422-
$statement = $analyzedSqlResults['statement'];
1423-
1424-
$matchedRowQuery = '';
1425-
if ($statement instanceof DeleteStatement) {
1426-
$matchedRowQuery = $this->getSimulatedDeleteQuery($analyzedSqlResults);
1427-
} elseif ($statement instanceof UpdateStatement) {
1428-
$matchedRowQuery = $this->getSimulatedUpdateQuery($analyzedSqlResults);
1429-
}
1430-
1431-
// Execute the query and get the number of matched rows.
1432-
$matchedRows = $this->executeMatchedRowQuery($matchedRowQuery);
1433-
1434-
// URL to matched rows.
1435-
$urlParams = [
1436-
'db' => $GLOBALS['db'],
1437-
'sql_query' => $matchedRowQuery,
1438-
'sql_signature' => Core::signSqlQuery($matchedRowQuery),
1439-
];
1440-
$matchedRowsUrl = Url::getFromRoute('/sql', $urlParams);
1441-
1442-
return [
1443-
'sql_query' => Html\Generator::formatSql($analyzedSqlResults['query']),
1444-
'matched_rows' => $matchedRows,
1445-
'matched_rows_url' => $matchedRowsUrl,
1446-
];
1447-
}
1448-
1449-
/**
1450-
* Transforms a UPDATE query into SELECT statement.
1451-
*
1452-
* @param array $analyzedSqlResults Analyzed SQL results from parser.
1453-
*
1454-
* @return string SQL query
1455-
*/
1456-
public function getSimulatedUpdateQuery(array $analyzedSqlResults): string
1457-
{
1458-
$tableReferences = Query::getTables($analyzedSqlResults['statement']);
1459-
1460-
$where = Query::getClause($analyzedSqlResults['statement'], $analyzedSqlResults['parser']->list, 'WHERE');
1461-
1462-
if (empty($where)) {
1463-
$where = '1';
1464-
}
1465-
1466-
$columns = [];
1467-
$diff = [];
1468-
foreach ($analyzedSqlResults['statement']->set as $set) {
1469-
$columns[] = $set->column;
1470-
$notEqualOperator = ' <> ';
1471-
if (strtoupper($set->value) === 'NULL') {
1472-
$notEqualOperator = ' IS NOT ';
1473-
}
1474-
1475-
$diff[] = $set->column . $notEqualOperator . $set->value;
1476-
}
1477-
1478-
if (! empty($diff)) {
1479-
$where .= ' AND (' . implode(' OR ', $diff) . ')';
1480-
}
1481-
1482-
$orderAndLimit = '';
1483-
1484-
if (! empty($analyzedSqlResults['statement']->order)) {
1485-
$orderAndLimit .= ' ORDER BY ' . Query::getClause(
1486-
$analyzedSqlResults['statement'],
1487-
$analyzedSqlResults['parser']->list,
1488-
'ORDER BY'
1489-
);
1490-
}
1491-
1492-
if (! empty($analyzedSqlResults['statement']->limit)) {
1493-
$orderAndLimit .= ' LIMIT ' . Query::getClause(
1494-
$analyzedSqlResults['statement'],
1495-
$analyzedSqlResults['parser']->list,
1496-
'LIMIT'
1497-
);
1498-
}
1499-
1500-
return 'SELECT ' . implode(', ', $columns) .
1501-
' FROM ' . implode(', ', $tableReferences) .
1502-
' WHERE ' . $where . $orderAndLimit;
1503-
}
1504-
1505-
/**
1506-
* Transforms a DELETE query into SELECT statement.
1507-
*
1508-
* @param array $analyzedSqlResults Analyzed SQL results from parser.
1509-
*
1510-
* @return string SQL query
1511-
*/
1512-
public function getSimulatedDeleteQuery(array $analyzedSqlResults): string
1513-
{
1514-
$tableReferences = Query::getTables($analyzedSqlResults['statement']);
1515-
1516-
$where = Query::getClause($analyzedSqlResults['statement'], $analyzedSqlResults['parser']->list, 'WHERE');
1517-
1518-
if (empty($where)) {
1519-
$where = '1';
1520-
}
1521-
1522-
$orderAndLimit = '';
1523-
1524-
if (! empty($analyzedSqlResults['statement']->order)) {
1525-
$orderAndLimit .= ' ORDER BY ' . Query::getClause(
1526-
$analyzedSqlResults['statement'],
1527-
$analyzedSqlResults['parser']->list,
1528-
'ORDER BY'
1529-
);
1530-
}
1531-
1532-
if (! empty($analyzedSqlResults['statement']->limit)) {
1533-
$orderAndLimit .= ' LIMIT ' . Query::getClause(
1534-
$analyzedSqlResults['statement'],
1535-
$analyzedSqlResults['parser']->list,
1536-
'LIMIT'
1537-
);
1538-
}
1539-
1540-
return 'SELECT * FROM ' . implode(', ', $tableReferences) .
1541-
' WHERE ' . $where . $orderAndLimit;
1542-
}
1543-
1544-
/**
1545-
* Executes the matched_row_query and returns the resultant row count.
1546-
*
1547-
* @param string $matchedRowQuery SQL query
1548-
*
1549-
* @return int Number of rows returned
1550-
*/
1551-
public function executeMatchedRowQuery(string $matchedRowQuery): int
1552-
{
1553-
global $dbi;
1554-
1555-
$dbi->selectDb($GLOBALS['db']);
1556-
// Execute the query.
1557-
$result = $dbi->tryQuery($matchedRowQuery);
1558-
// Count the number of rows in the result set.
1559-
$result = $dbi->numRows($result);
1560-
1561-
return $result;
1562-
}
1563-
15641344
/**
15651345
* Handles request for ROLLBACK.
15661346
*

0 commit comments

Comments
 (0)