|
10 | 10 | use PhpMyAdmin\Import\SimulateDml; |
11 | 11 | use PhpMyAdmin\Message; |
12 | 12 | use PhpMyAdmin\ResponseRenderer; |
| 13 | +use PhpMyAdmin\SqlParser\Lexer; |
13 | 14 | use PhpMyAdmin\SqlParser\Parser; |
14 | 15 | use PhpMyAdmin\SqlParser\Statements\DeleteStatement; |
15 | 16 | use PhpMyAdmin\SqlParser\Statements\UpdateStatement; |
| 17 | +use PhpMyAdmin\SqlParser\TokensList; |
| 18 | +use PhpMyAdmin\SqlParser\TokenType; |
16 | 19 | use PhpMyAdmin\SqlParser\Utils\Query; |
17 | 20 |
|
18 | 21 | use function __; |
| 22 | +use function array_filter; |
| 23 | +use function array_values; |
19 | 24 | use function count; |
20 | | -use function explode; |
21 | 25 |
|
22 | 26 | final class SimulateDmlController implements InvocableController |
23 | 27 | { |
| 28 | + private string $error = ''; |
| 29 | + |
| 30 | + /** |
| 31 | + * @psalm-var list<array{ |
| 32 | + * sql_query: string, |
| 33 | + * matched_rows: int, |
| 34 | + * matched_rows_url: string, |
| 35 | + * }> |
| 36 | + */ |
| 37 | + private array $data = []; |
| 38 | + |
24 | 39 | public function __construct(private readonly ResponseRenderer $response, private readonly SimulateDml $simulateDml) |
25 | 40 | { |
26 | 41 | } |
27 | 42 |
|
28 | 43 | public function __invoke(ServerRequest $request): Response |
29 | 44 | { |
30 | | - $error = ''; |
31 | | - $errorMsg = __('Only single-table UPDATE and DELETE queries can be simulated.'); |
32 | 45 | /** @var string $sqlDelimiter */ |
33 | 46 | $sqlDelimiter = $request->getParsedBodyParam('sql_delimiter', ''); |
34 | | - $sqlData = []; |
35 | | - $queries = explode($sqlDelimiter, $GLOBALS['sql_query']); |
36 | | - foreach ($queries as $sqlQuery) { |
37 | | - if ($sqlQuery === '') { |
38 | | - continue; |
39 | | - } |
40 | 47 |
|
41 | | - // Parsing the query. |
42 | | - $parser = new Parser($sqlQuery); |
| 48 | + $parser = $this->createParser($GLOBALS['sql_query'], $sqlDelimiter); |
| 49 | + $this->process($parser); |
43 | 50 |
|
44 | | - if (empty($parser->statements[0])) { |
45 | | - continue; |
46 | | - } |
| 51 | + if ($this->error !== '') { |
| 52 | + $this->response->addJSON('message', Message::rawError($this->error)); |
| 53 | + $this->response->addJSON('sql_data', false); |
47 | 54 |
|
48 | | - $statement = $parser->statements[0]; |
| 55 | + return $this->response->response(); |
| 56 | + } |
| 57 | + |
| 58 | + $this->response->addJSON('sql_data', $this->data); |
| 59 | + |
| 60 | + return $this->response->response(); |
| 61 | + } |
| 62 | + |
| 63 | + private function createParser(string $query, string $delimiter): Parser |
| 64 | + { |
| 65 | + $lexer = new Lexer($query, false, $delimiter); |
| 66 | + $list = new TokensList(array_values(array_filter( |
| 67 | + $lexer->list->tokens, |
| 68 | + static function ($token): bool { |
| 69 | + return $token->type !== TokenType::Comment; |
| 70 | + }, |
| 71 | + ))); |
| 72 | + |
| 73 | + return new Parser($list); |
| 74 | + } |
49 | 75 |
|
| 76 | + private function process(Parser $parser): void |
| 77 | + { |
| 78 | + foreach ($parser->statements as $statement) { |
50 | 79 | if ( |
51 | | - ! ($statement instanceof UpdateStatement || $statement instanceof DeleteStatement) |
| 80 | + ! $statement instanceof UpdateStatement && ! $statement instanceof DeleteStatement |
52 | 81 | || ! empty($statement->join) |
| 82 | + || count(Query::getTables($statement)) > 1 |
53 | 83 | ) { |
54 | | - $error = $errorMsg; |
55 | | - break; |
56 | | - } |
57 | | - |
58 | | - $tables = Query::getTables($statement); |
59 | | - if (count($tables) > 1) { |
60 | | - $error = $errorMsg; |
| 84 | + $this->error = __('Only single-table UPDATE and DELETE queries can be simulated.'); |
61 | 85 | break; |
62 | 86 | } |
63 | 87 |
|
64 | 88 | // Get the matched rows for the query. |
65 | | - $result = $this->simulateDml->getMatchedRows($sqlQuery, $parser, $statement); |
66 | | - $error = $this->simulateDml->getError(); |
| 89 | + $result = $this->simulateDml->getMatchedRows($parser, $statement); |
| 90 | + $this->error = $this->simulateDml->getError(); |
67 | 91 |
|
68 | | - if ($error !== '') { |
| 92 | + if ($this->error !== '') { |
69 | 93 | break; |
70 | 94 | } |
71 | 95 |
|
72 | | - $sqlData[] = $result; |
| 96 | + $this->data[] = $result; |
73 | 97 | } |
74 | | - |
75 | | - if ($error !== '') { |
76 | | - $message = Message::rawError($error); |
77 | | - $this->response->addJSON('message', $message); |
78 | | - $this->response->addJSON('sql_data', false); |
79 | | - |
80 | | - return $this->response->response(); |
81 | | - } |
82 | | - |
83 | | - $this->response->addJSON('sql_data', $sqlData); |
84 | | - |
85 | | - return $this->response->response(); |
86 | 98 | } |
87 | 99 | } |
0 commit comments