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