From 349a8116a2cd51b5483947a17cae7330eaa5cb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Wed, 27 Sep 2023 11:47:31 -0300 Subject: [PATCH 1/2] Add test for #498 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MaurĂ­cio Meneghini Fauth --- tests/Misc/BugsTest.php | 1 + tests/data/bugs/gh498.in | 4 + tests/data/bugs/gh498.out | 423 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 tests/data/bugs/gh498.in create mode 100644 tests/data/bugs/gh498.out diff --git a/tests/Misc/BugsTest.php b/tests/Misc/BugsTest.php index 88606d867..2f93e5c17 100644 --- a/tests/Misc/BugsTest.php +++ b/tests/Misc/BugsTest.php @@ -35,6 +35,7 @@ public function bugProvider(): array ['bugs/gh412'], ['bugs/gh478'], ['bugs/gh492'], + ['bugs/gh498'], ['bugs/gh499'], ['bugs/gh508'], ['bugs/gh511'], diff --git a/tests/data/bugs/gh498.in b/tests/data/bugs/gh498.in new file mode 100644 index 000000000..8c3d72d92 --- /dev/null +++ b/tests/data/bugs/gh498.in @@ -0,0 +1,4 @@ +SELECT ? +FROM uno +JOIN dos ON dos.id = uno.id +LIMIT ? OFFSET ? \ No newline at end of file diff --git a/tests/data/bugs/gh498.out b/tests/data/bugs/gh498.out new file mode 100644 index 000000000..44fbc5a21 --- /dev/null +++ b/tests/data/bugs/gh498.out @@ -0,0 +1,423 @@ +{ + "query": "SELECT ?\nFROM uno\nJOIN dos ON dos.id = uno.id\nLIMIT ? OFFSET ?", + "lexer": { + "@type": "PhpMyAdmin\\SqlParser\\Lexer", + "str": "SELECT ?\nFROM uno\nJOIN dos ON dos.id = uno.id\nLIMIT ? OFFSET ?", + "len": 63, + "last": 63, + "list": { + "@type": "PhpMyAdmin\\SqlParser\\TokensList", + "tokens": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "SELECT", + "value": "SELECT", + "keyword": "SELECT", + "type": 1, + "flags": 3, + "position": 0 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 6 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "?", + "value": "?", + "keyword": null, + "type": 8, + "flags": 16, + "position": 7 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 8 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "FROM", + "value": "FROM", + "keyword": "FROM", + "type": 1, + "flags": 3, + "position": 9 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 13 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "uno", + "value": "uno", + "keyword": null, + "type": 0, + "flags": 0, + "position": 14 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 17 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "JOIN", + "value": "JOIN", + "keyword": "JOIN", + "type": 1, + "flags": 3, + "position": 18 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 22 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "dos", + "value": "dos", + "keyword": null, + "type": 0, + "flags": 0, + "position": 23 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 26 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "ON", + "value": "ON", + "keyword": "ON", + "type": 1, + "flags": 3, + "position": 28 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 30 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "dos", + "value": "dos", + "keyword": null, + "type": 0, + "flags": 0, + "position": 31 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ".", + "value": ".", + "keyword": null, + "type": 2, + "flags": 16, + "position": 34 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "id", + "value": "id", + "keyword": null, + "type": 0, + "flags": 0, + "position": 35 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 37 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "=", + "value": "=", + "keyword": null, + "type": 2, + "flags": 2, + "position": 38 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 39 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "uno", + "value": "uno", + "keyword": null, + "type": 0, + "flags": 0, + "position": 40 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ".", + "value": ".", + "keyword": null, + "type": 2, + "flags": 16, + "position": 43 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "id", + "value": "id", + "keyword": null, + "type": 0, + "flags": 0, + "position": 44 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 46 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "LIMIT", + "value": "LIMIT", + "keyword": "LIMIT", + "type": 1, + "flags": 3, + "position": 47 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 52 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "?", + "value": "?", + "keyword": null, + "type": 8, + "flags": 16, + "position": 53 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 54 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "OFFSET", + "value": "OFFSET", + "keyword": "OFFSET", + "type": 1, + "flags": 1, + "position": 55 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 61 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "?", + "value": "?", + "keyword": null, + "type": 8, + "flags": 16, + "position": 62 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": null, + "value": null, + "keyword": null, + "type": 9, + "flags": 0, + "position": null + } + ], + "count": 32, + "idx": 32 + }, + "delimiter": ";", + "delimiterLen": 1, + "strict": false, + "errors": [] + }, + "parser": { + "@type": "PhpMyAdmin\\SqlParser\\Parser", + "list": { + "@type": "@1" + }, + "statements": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Statements\\SelectStatement", + "expr": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": null, + "expr": "?", + "alias": null, + "function": null, + "subquery": null + } + ], + "from": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "uno", + "column": null, + "expr": "uno", + "alias": null, + "function": null, + "subquery": null + } + ], + "index_hints": null, + "partition": null, + "where": null, + "group": null, + "group_options": null, + "having": null, + "order": null, + "limit": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Limit", + "offset": 0, + "rowCount": 0 + }, + "procedure": null, + "into": null, + "join": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\JoinKeyword", + "type": "JOIN", + "expr": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "dos", + "column": null, + "expr": "dos", + "alias": null, + "function": null, + "subquery": null + }, + "on": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Condition", + "identifiers": [ + "dos", + "id", + "uno" + ], + "isOperator": false, + "expr": "dos.id = uno.id" + } + ], + "using": null + } + ], + "union": [], + "end_options": null, + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [] + }, + "first": 0, + "last": 30 + } + ], + "brackets": 0, + "strict": false, + "errors": [] + }, + "errors": { + "lexer": [], + "parser": [ + [ + "Unexpected token.", + { + "@type": "@28" + }, + 0 + ], + [ + "Unrecognized keyword.", + { + "@type": "@30" + }, + 0 + ], + [ + "Unexpected token.", + { + "@type": "@32" + }, + 0 + ] + ] + } +} \ No newline at end of file From 66219412c4d2b11b46f9e404071987441d9daedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Wed, 27 Sep 2023 11:52:22 -0300 Subject: [PATCH 2/2] Fix bind parameter in LIMIT OFFSET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixes #498 Signed-off-by: MaurĂ­cio Meneghini Fauth --- phpstan-baseline.neon | 4 ++-- src/Components/Limit.php | 15 +++++++++------ tests/data/bugs/gh498.out | 28 +++------------------------- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0d1a703fe..cb9ed9efc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -271,12 +271,12 @@ parameters: path: src/Components/Key.php - - message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\:\\:\\$offset \\(int\\) does not accept mixed\\.$#" + message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\:\\:\\$offset \\(int\\|string\\) does not accept mixed\\.$#" count: 1 path: src/Components/Limit.php - - message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\:\\:\\$rowCount \\(int\\) does not accept mixed\\.$#" + message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\:\\:\\$rowCount \\(int\\|string\\) does not accept mixed\\.$#" count: 1 path: src/Components/Limit.php diff --git a/src/Components/Limit.php b/src/Components/Limit.php index af4ff5923..172fd3074 100644 --- a/src/Components/Limit.php +++ b/src/Components/Limit.php @@ -19,20 +19,20 @@ class Limit extends Component /** * The number of rows skipped. * - * @var int + * @var int|string */ public $offset; /** * The number of rows to be returned. * - * @var int + * @var int|string */ public $rowCount; /** - * @param int $rowCount the row count - * @param int $offset the offset + * @param int|string $rowCount the row count + * @param int|string $offset the offset */ public function __construct($rowCount = 0, $offset = 0) { @@ -88,8 +88,11 @@ public static function parse(Parser $parser, TokensList $list, array $options = continue; } - // Skip if not a number - if (($token->type !== Token::TYPE_NUMBER)) { + // Skip if not a number or a bind parameter (?) + if ( + ! ($token->type === Token::TYPE_NUMBER + || ($token->type === Token::TYPE_SYMBOL && ($token->flags & Token::FLAG_SYMBOL_PARAMETER))) + ) { break; } diff --git a/tests/data/bugs/gh498.out b/tests/data/bugs/gh498.out index 44fbc5a21..1b4e5d533 100644 --- a/tests/data/bugs/gh498.out +++ b/tests/data/bugs/gh498.out @@ -346,8 +346,8 @@ "order": null, "limit": { "@type": "PhpMyAdmin\\SqlParser\\Components\\Limit", - "offset": 0, - "rowCount": 0 + "offset": "?", + "rowCount": "?" }, "procedure": null, "into": null, @@ -396,28 +396,6 @@ }, "errors": { "lexer": [], - "parser": [ - [ - "Unexpected token.", - { - "@type": "@28" - }, - 0 - ], - [ - "Unrecognized keyword.", - { - "@type": "@30" - }, - 0 - ], - [ - "Unexpected token.", - { - "@type": "@32" - }, - 0 - ] - ] + "parser": [] } } \ No newline at end of file