diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 79f15b246..27d0e7ce3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1185,16 +1185,6 @@ parameters: count: 1 path: tests/Parser/WithStatementTest.php - - - message: "#^Parameter \\#1 \\$statement of static method PhpMyAdmin\\\\SqlParser\\\\Utils\\\\Misc\\:\\:getAliases\\(\\) expects PhpMyAdmin\\\\SqlParser\\\\Statements\\\\SelectStatement, PhpMyAdmin\\\\SqlParser\\\\Statement\\|null given\\.$#" - count: 1 - path: tests/Utils/MiscTest.php - - - - message: "#^Parameter \\#2 \\$database of static method PhpMyAdmin\\\\SqlParser\\\\Utils\\\\Misc\\:\\:getAliases\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: tests/Utils/MiscTest.php - - message: "#^Parameter \\#2 \\$list of static method PhpMyAdmin\\\\SqlParser\\\\Utils\\\\Query\\:\\:getClause\\(\\) expects PhpMyAdmin\\\\SqlParser\\\\TokensList, PhpMyAdmin\\\\SqlParser\\\\TokensList\\|null given\\.$#" count: 9 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 36ffe79a8..c8719a292 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -5177,6 +5177,9 @@ + + $tables[$thisDb] + $endOptions $groupOptions @@ -5419,14 +5422,6 @@ type === Token::TYPE_KEYWORD]]> - - - ! ($statement instanceof SelectStatement) - - - $tables[$thisDb] - - int @@ -5735,15 +5730,6 @@ setAccessible - - - $statement - - - $db - $statement - - list]]> diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php index 6aad579f8..cee5b3555 100644 --- a/src/Statements/SelectStatement.php +++ b/src/Statements/SelectStatement.php @@ -366,4 +366,85 @@ public function getClauses() return static::$clauses; } + + /** + * Gets a list of all aliases and their original names. + * + * @param string $database the name of the database + * + * @return array|string|null>>|null>> + */ + public function getAliases(string $database): array + { + if (empty($this->expr) || empty($this->from)) { + return []; + } + + $retval = []; + + $tables = []; + + /** + * Expressions that may contain aliases. + * These are extracted from `FROM` and `JOIN` keywords. + */ + $expressions = $this->from; + + // Adding expressions from JOIN. + if (! empty($this->join)) { + foreach ($this->join as $join) { + $expressions[] = $join->expr; + } + } + + foreach ($expressions as $expr) { + if (! isset($expr->table) || ($expr->table === '')) { + continue; + } + + $thisDb = isset($expr->database) && ($expr->database !== '') ? + $expr->database : $database; + + if (! isset($retval[$thisDb])) { + $retval[$thisDb] = [ + 'alias' => null, + 'tables' => [], + ]; + } + + if (! isset($retval[$thisDb]['tables'][$expr->table])) { + $retval[$thisDb]['tables'][$expr->table] = [ + 'alias' => isset($expr->alias) && ($expr->alias !== '') ? + $expr->alias : null, + 'columns' => [], + ]; + } + + if (! isset($tables[$thisDb])) { + $tables[$thisDb] = []; + } + + $tables[$thisDb][$expr->alias] = $expr->table; + } + + foreach ($this->expr as $expr) { + if (! isset($expr->column, $expr->alias) || ($expr->column === '') || ($expr->alias === '')) { + continue; + } + + $thisDb = isset($expr->database) && ($expr->database !== '') ? + $expr->database : $database; + + if (isset($expr->table) && ($expr->table !== '')) { + $thisTable = $tables[$thisDb][$expr->table] ?? $expr->table; + $retval[$thisDb]['tables'][$thisTable]['columns'][$expr->column] = $expr->alias; + } else { + foreach ($retval[$thisDb]['tables'] as &$table) { + $table['columns'][$expr->column] = $expr->alias; + } + } + } + + return $retval; + } } diff --git a/src/Utils/Misc.php b/src/Utils/Misc.php deleted file mode 100644 index f4e0bcd83..000000000 --- a/src/Utils/Misc.php +++ /dev/null @@ -1,98 +0,0 @@ -|string|null>>|null>> - */ - public static function getAliases($statement, $database) - { - if (! ($statement instanceof SelectStatement) || empty($statement->expr) || empty($statement->from)) { - return []; - } - - $retval = []; - - $tables = []; - - /** - * Expressions that may contain aliases. - * These are extracted from `FROM` and `JOIN` keywords. - * - * @var Expression[] - */ - $expressions = $statement->from; - - // Adding expressions from JOIN. - if (! empty($statement->join)) { - foreach ($statement->join as $join) { - $expressions[] = $join->expr; - } - } - - foreach ($expressions as $expr) { - if (! isset($expr->table) || ($expr->table === '')) { - continue; - } - - $thisDb = isset($expr->database) && ($expr->database !== '') ? - $expr->database : $database; - - if (! isset($retval[$thisDb])) { - $retval[$thisDb] = [ - 'alias' => null, - 'tables' => [], - ]; - } - - if (! isset($retval[$thisDb]['tables'][$expr->table])) { - $retval[$thisDb]['tables'][$expr->table] = [ - 'alias' => isset($expr->alias) && ($expr->alias !== '') ? - $expr->alias : null, - 'columns' => [], - ]; - } - - if (! isset($tables[$thisDb])) { - $tables[$thisDb] = []; - } - - $tables[$thisDb][$expr->alias] = $expr->table; - } - - foreach ($statement->expr as $expr) { - if (! isset($expr->column, $expr->alias) || ($expr->column === '') || ($expr->alias === '')) { - continue; - } - - $thisDb = isset($expr->database) && ($expr->database !== '') ? - $expr->database : $database; - - if (isset($expr->table) && ($expr->table !== '')) { - $thisTable = $tables[$thisDb][$expr->table] ?? $expr->table; - $retval[$thisDb]['tables'][$thisTable]['columns'][$expr->column] = $expr->alias; - } else { - foreach ($retval[$thisDb]['tables'] as &$table) { - $table['columns'][$expr->column] = $expr->alias; - } - } - } - - return $retval; - } -} diff --git a/tests/Builder/StatementTest.php b/tests/Builder/StatementTest.php index ea483ccda..01a127c46 100644 --- a/tests/Builder/StatementTest.php +++ b/tests/Builder/StatementTest.php @@ -8,6 +8,7 @@ use PhpMyAdmin\SqlParser\Components\Expression; use PhpMyAdmin\SqlParser\Components\Limit; use PhpMyAdmin\SqlParser\Components\OptionsArray; +use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\SqlParser\Statements\SelectStatement; use PhpMyAdmin\SqlParser\Tests\TestCase; @@ -39,4 +40,121 @@ public function testBuilder(): void (string) $stmt ); } + + /** + * @psalm-param array}> + * }> $expected + * + * @dataProvider getAliasesProvider + */ + public function testGetAliases(string $query, string $db, array $expected): void + { + $parser = new Parser($query); + $this->assertInstanceOf(SelectStatement::class, $parser->statements[0]); + $this->assertEquals($expected, $parser->statements[0]->getAliases($db)); + } + + /** + * @psalm-return list}> + * }>}> + */ + public static function getAliasesProvider(): array + { + return [ + [ + 'select * from (select 1) tbl', + 'mydb', + [], + ], + [ + 'select i.name as `n`,abcdef gh from qwerty i', + 'mydb', + [ + 'mydb' => [ + 'alias' => null, + 'tables' => [ + 'qwerty' => [ + 'alias' => 'i', + 'columns' => [ + 'name' => 'n', + 'abcdef' => 'gh', + ], + ], + ], + ], + ], + ], + [ + 'select film_id id,title from film', + 'sakila', + [ + 'sakila' => [ + 'alias' => null, + 'tables' => [ + 'film' => [ + 'alias' => null, + 'columns' => ['film_id' => 'id'], + ], + ], + ], + ], + ], + [ + 'select `sakila`.`A`.`actor_id` as aid,`F`.`film_id` `fid`,' + . 'last_update updated from `sakila`.actor A join `film_actor` as ' + . '`F` on F.actor_id = A.`actor_id`', + 'sakila', + [ + 'sakila' => [ + 'alias' => null, + 'tables' => [ + 'film_actor' => [ + 'alias' => 'F', + 'columns' => [ + 'film_id' => 'fid', + 'last_update' => 'updated', + ], + ], + 'actor' => [ + 'alias' => 'A', + 'columns' => [ + 'actor_id' => 'aid', + 'last_update' => 'updated', + ], + ], + ], + ], + ], + ], + [ + 'SELECT film_id FROM (SELECT * FROM film) as f;', + 'sakila', + [], + ], + [ + 'SELECT 1', + '', + [], + ], + [ + 'SELECT * FROM orders AS ord WHERE 1', + 'db', + [ + 'db' => [ + 'alias' => null, + 'tables' => [ + 'orders' => [ + 'alias' => 'ord', + 'columns' => [], + ], + ], + ], + ], + ], + ]; + } } diff --git a/tests/Utils/MiscTest.php b/tests/Utils/MiscTest.php deleted file mode 100644 index 338628eb6..000000000 --- a/tests/Utils/MiscTest.php +++ /dev/null @@ -1,137 +0,0 @@ -}> - * }> $expected - * - * @dataProvider getAliasesProvider - */ - public function testGetAliases(string $query, string|null $db, array $expected): void - { - $parser = new Parser($query); - $statement = empty($parser->statements[0]) ? - null : $parser->statements[0]; - $this->assertEquals($expected, Misc::getAliases($statement, $db)); - } - - /** - * @return array> - * @psalm-return list}> - * }>}> - */ - public static function getAliasesProvider(): array - { - return [ - [ - 'select * from (select 1) tbl', - 'mydb', - [], - ], - [ - 'select i.name as `n`,abcdef gh from qwerty i', - 'mydb', - [ - 'mydb' => [ - 'alias' => null, - 'tables' => [ - 'qwerty' => [ - 'alias' => 'i', - 'columns' => [ - 'name' => 'n', - 'abcdef' => 'gh', - ], - ], - ], - ], - ], - ], - [ - 'select film_id id,title from film', - 'sakila', - [ - 'sakila' => [ - 'alias' => null, - 'tables' => [ - 'film' => [ - 'alias' => null, - 'columns' => ['film_id' => 'id'], - ], - ], - ], - ], - ], - [ - 'select `sakila`.`A`.`actor_id` as aid,`F`.`film_id` `fid`,' - . 'last_update updated from `sakila`.actor A join `film_actor` as ' - . '`F` on F.actor_id = A.`actor_id`', - 'sakila', - [ - 'sakila' => [ - 'alias' => null, - 'tables' => [ - 'film_actor' => [ - 'alias' => 'F', - 'columns' => [ - 'film_id' => 'fid', - 'last_update' => 'updated', - ], - ], - 'actor' => [ - 'alias' => 'A', - 'columns' => [ - 'actor_id' => 'aid', - 'last_update' => 'updated', - ], - ], - ], - ], - ], - ], - [ - 'SELECT film_id FROM (SELECT * FROM film) as f;', - 'sakila', - [], - ], - [ - '', - null, - [], - ], - [ - 'SELECT 1', - null, - [], - ], - [ - 'SELECT * FROM orders AS ord WHERE 1', - 'db', - [ - 'db' => [ - 'alias' => null, - 'tables' => [ - 'orders' => [ - 'alias' => 'ord', - 'columns' => [], - ], - ], - ], - ], - ], - ]; - } -}