Skip to content

Commit 9e99de5

Browse files
committed
Merge branch '5.8.x'
Signed-off-by: William Desportes <[email protected]>
2 parents a7a57cd + 963cfb0 commit 9e99de5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3285
-122
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
- Support `KEY` on `CreateDefinition` (#330)
2424
- Fix `CALL` statements parsing (#372)
2525
- Implement support for `LEFT JOIN`, `JOIN`, `INNER JOIN` on `UpdateStatement` (#260)
26+
- Implement support for `TABLE` and `REPLACE` statements on `DESCRIBE` statements
27+
- Fix `DESCRIBE` to allow a schema.table syntax (#445)
2628

2729
## [5.7.0] - 2023-01-25
2830

phpstan-baseline.neon

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -711,22 +711,27 @@ parameters:
711711
path: src/Statements/DeleteStatement.php
712712

713713
-
714-
message: "#^Cannot access property \\$keyword on PhpMyAdmin\\\\SqlParser\\\\Token\\|null\\.$#"
714+
message: "#^Binary operation \"\\.\" between ' ' and array\\<string\\>\\|string results in an error\\.$#"
715715
count: 1
716716
path: src/Statements/ExplainStatement.php
717717

718718
-
719-
message: "#^Cannot access property \\$options on PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\.$#"
719+
message: "#^Binary operation \"\\.\" between array\\<string\\>\\|string and '\\.' results in an error\\.$#"
720720
count: 1
721721
path: src/Statements/ExplainStatement.php
722722

723723
-
724-
message: "#^Cannot access property \\$value on PhpMyAdmin\\\\SqlParser\\\\Token\\|null\\.$#"
724+
message: "#^Binary operation \"\\.\\=\" between string and array\\<string\\>\\|string results in an error\\.$#"
725725
count: 1
726726
path: src/Statements/ExplainStatement.php
727727

728728
-
729-
message: "#^Parameter \\#1 \\$component of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\:\\:build\\(\\) expects PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray, PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null given\\.$#"
729+
message: "#^Cannot access property \\$keyword on PhpMyAdmin\\\\SqlParser\\\\Token\\|null\\.$#"
730+
count: 1
731+
path: src/Statements/ExplainStatement.php
732+
733+
-
734+
message: "#^Cannot access property \\$value on PhpMyAdmin\\\\SqlParser\\\\Token\\|null\\.$#"
730735
count: 1
731736
path: src/Statements/ExplainStatement.php
732737

@@ -735,6 +740,16 @@ parameters:
735740
count: 1
736741
path: src/Statements/ExplainStatement.php
737742

743+
-
744+
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statements\\\\ExplainStatement\\:\\:\\$explainedColumn \\(string\\|null\\) does not accept mixed\\.$#"
745+
count: 1
746+
path: src/Statements/ExplainStatement.php
747+
748+
-
749+
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statements\\\\ExplainStatement\\:\\:\\$explainedDatabase \\(string\\|null\\) does not accept mixed\\.$#"
750+
count: 1
751+
path: src/Statements/ExplainStatement.php
752+
738753
-
739754
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statements\\\\ExplainStatement\\:\\:\\$explainedTable \\(string\\|null\\) does not accept mixed\\.$#"
740755
count: 1

psalm-baseline.xml

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@
191191
<InvalidArgument>
192192
<code><![CDATA[$options['breakOnParentheses']]]></code>
193193
</InvalidArgument>
194+
<InvalidAttribute>
195+
<code>\AllowDynamicProperties</code>
196+
</InvalidAttribute>
194197
<MixedArrayOffset>
195198
<code><![CDATA[Parser::$statementParsers[$token->keyword]]]></code>
196199
<code><![CDATA[self::$allowedKeywords[$token->keyword]]]></code>
@@ -233,9 +236,6 @@
233236
<TypeDoesNotContainType>
234237
<code><![CDATA[empty($options['field'])]]></code>
235238
</TypeDoesNotContainType>
236-
<UndefinedAttributeClass>
237-
<code>\AllowDynamicProperties</code>
238-
</UndefinedAttributeClass>
239239
</file>
240240
<file src="src/Components/ExpressionArray.php">
241241
<InvalidArgument>
@@ -4917,6 +4917,9 @@
49174917
<code>$built[$field]</code>
49184918
<code><![CDATA[$parsedClauses[$token->value]]]></code>
49194919
</InvalidArgument>
4920+
<InvalidAttribute>
4921+
<code>\AllowDynamicProperties</code>
4922+
</InvalidAttribute>
49204923
<MethodSignatureMustProvideReturnType>
49214924
<code>__toString</code>
49224925
</MethodSignatureMustProvideReturnType>
@@ -4952,9 +4955,6 @@
49524955
<TypeDoesNotContainType>
49534956
<code>count($clauses) === 0</code>
49544957
</TypeDoesNotContainType>
4955-
<UndefinedAttributeClass>
4956-
<code>\AllowDynamicProperties</code>
4957-
</UndefinedAttributeClass>
49584958
<UnusedForeachValue>
49594959
<code>$index</code>
49604960
</UnusedForeachValue>
@@ -5089,16 +5089,19 @@
50895089
<file src="src/Statements/ExplainStatement.php">
50905090
<MixedAssignment>
50915091
<code><![CDATA[$this->connectionId]]></code>
5092+
<code><![CDATA[$this->explainedColumn]]></code>
5093+
<code><![CDATA[$this->explainedDatabase]]></code>
50925094
<code><![CDATA[$this->explainedTable]]></code>
50935095
</MixedAssignment>
5094-
<PossiblyNullArgument>
5095-
<code><![CDATA[$this->options]]></code>
5096-
<code><![CDATA[$this->options->options]]></code>
5097-
</PossiblyNullArgument>
5096+
<PossiblyInvalidOperand>
5097+
<code><![CDATA[Context::escape($this->explainedColumn)]]></code>
5098+
<code><![CDATA[Context::escape($this->explainedDatabase)]]></code>
5099+
<code><![CDATA[Context::escape($this->explainedTable)]]></code>
5100+
<code><![CDATA[Context::escape($this->explainedTable)]]></code>
5101+
</PossiblyInvalidOperand>
50985102
<PossiblyNullPropertyFetch>
50995103
<code><![CDATA[$nextToken->keyword]]></code>
51005104
<code><![CDATA[$nextToken->value]]></code>
5101-
<code><![CDATA[$this->options->options]]></code>
51025105
</PossiblyNullPropertyFetch>
51035106
<PropertyNotSetInConstructor>
51045107
<code>$statementAlias</code>

src/Statements/ExplainStatement.php

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpMyAdmin\SqlParser\Statements;
66

77
use PhpMyAdmin\SqlParser\Components\OptionsArray;
8+
use PhpMyAdmin\SqlParser\Context;
89
use PhpMyAdmin\SqlParser\Parser;
910
use PhpMyAdmin\SqlParser\Statement;
1011
use PhpMyAdmin\SqlParser\Token;
@@ -58,13 +59,27 @@ class ExplainStatement extends Statement
5859
*/
5960
public $connectionId = null;
6061

62+
/**
63+
* The explained database for the table's name, if used.
64+
*
65+
* @var string|null
66+
*/
67+
public $explainedDatabase = null;
68+
6169
/**
6270
* The explained table's name, if used.
6371
*
6472
* @var string|null
6573
*/
6674
public $explainedTable = null;
6775

76+
/**
77+
* The explained column's name, if used.
78+
*
79+
* @var string|null
80+
*/
81+
public $explainedColumn = null;
82+
6883
/**
6984
* @param Parser $parser the instance that requests parsing
7085
* @param TokensList $list the list of tokens to be parsed
@@ -78,10 +93,14 @@ public function parse(Parser $parser, TokensList $list)
7893
*
7994
* 0 -------------------[ EXPLAIN/EXPLAIN ANALYZE/ANALYZE ]-----------------------> 1
8095
*
96+
* 0 ------------------------[ EXPLAIN/DESC/DESCRIBE ]----------------------------> 3
97+
*
8198
* 1 ------------------------------[ OPTIONS ]------------------------------------> 2
8299
*
83100
* 2 --------------[ tablename / STATEMENT / FOR CONNECTION ]---------------------> 2
84101
*
102+
* 3 -----------------------------[ tablename ]-----------------------------------> 3
103+
*
85104
* @var int
86105
*/
87106
$state = 0;
@@ -100,6 +119,12 @@ public function parse(Parser $parser, TokensList $list)
100119
*/
101120
$token = $list->tokens[$list->idx];
102121

122+
// End of statement.
123+
if ($token->type === Token::TYPE_DELIMITER) {
124+
--$list->idx; // Back up one token, no real reasons to document
125+
break;
126+
}
127+
103128
// Skipping whitespaces and comments.
104129
if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) {
105130
continue;
@@ -114,9 +139,21 @@ public function parse(Parser $parser, TokensList $list)
114139
|| $token->keyword === 'DESC'
115140
|| $token->keyword === 'DESCRIBE'
116141
) {
117-
$miniState = 1;
118142
$this->statementAlias = $token->keyword;
119143

144+
$lastIdx = $list->idx;
145+
$list->idx++; // Ignore the current token
146+
$nextKeyword = $list->getNextOfType(Token::TYPE_KEYWORD);
147+
$list->idx = $lastIdx;
148+
149+
// There is no other keyword, we must be describing a table
150+
if ($nextKeyword === null) {
151+
$state = 3;
152+
continue;
153+
}
154+
155+
$miniState = 1;
156+
120157
$lastIdx = $list->idx;
121158
$nextKeyword = $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ANALYZE');
122159
if ($nextKeyword && $nextKeyword->keyword !== null) {
@@ -146,15 +183,11 @@ public function parse(Parser $parser, TokensList $list)
146183
break;
147184
}
148185

149-
// To support EXPLAIN tablename
150-
if ($token->type === Token::TYPE_NONE) {
151-
$this->explainedTable = $token->value;
152-
break;
153-
}
154-
155186
if (
156187
$token->keyword !== 'SELECT'
188+
&& $token->keyword !== 'TABLE'
157189
&& $token->keyword !== 'INSERT'
190+
&& $token->keyword !== 'REPLACE'
158191
&& $token->keyword !== 'UPDATE'
159192
&& $token->keyword !== 'DELETE'
160193
) {
@@ -178,28 +211,74 @@ public function parse(Parser $parser, TokensList $list)
178211

179212
$list->idx = $idxOfLastParsedToken;
180213
break;
214+
} elseif ($state === 3) {
215+
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) {
216+
continue;
217+
}
218+
219+
if ($this->explainedDatabase === null) {
220+
$lastIdx = $list->idx;
221+
$nextDot = $list->getNextOfTypeAndValue(Token::TYPE_OPERATOR, '.');
222+
$list->idx = $lastIdx;
223+
if ($nextDot !== null) {// We found a dot, so it must be a db.table name format
224+
$this->explainedDatabase = $token->value;
225+
continue;
226+
}
227+
}
228+
229+
if ($this->explainedTable === null) {
230+
$this->explainedTable = $token->value;
231+
continue;
232+
}
233+
234+
if ($this->explainedColumn === null) {
235+
$this->explainedColumn = $token->value;
236+
}
181237
}
182238
}
239+
240+
if ($state !== 3 || $this->explainedTable !== null) {
241+
return;
242+
}
243+
244+
// We reached end of the state 3 and no table name was found
245+
/** Token parsed at this moment. */
246+
$token = $list->tokens[$list->idx];
247+
$parser->error('Expected a table name.', $token);
183248
}
184249

185250
public function build(): string
186251
{
187252
$str = $this->statementAlias;
188253

189-
if (count($this->options->options)) {
190-
$str .= ' ';
254+
if ($this->options !== null) {
255+
if (count($this->options->options)) {
256+
$str .= ' ';
257+
}
258+
259+
$str .= OptionsArray::build($this->options) . ' ';
191260
}
192261

193-
$str .= OptionsArray::build($this->options) . ' ';
262+
if ($this->options === null) {
263+
$str .= ' ';
264+
}
194265

195266
if ($this->bodyParser) {
196267
foreach ($this->bodyParser->statements as $statement) {
197268
$str .= $statement->build();
198269
}
199270
} elseif ($this->connectionId) {
200271
$str .= 'FOR CONNECTION ' . $this->connectionId;
201-
} elseif ($this->explainedTable) {
202-
$str .= $this->explainedTable;
272+
}
273+
274+
if ($this->explainedDatabase !== null && $this->explainedTable !== null) {
275+
$str .= Context::escape($this->explainedDatabase) . '.' . Context::escape($this->explainedTable);
276+
} elseif ($this->explainedTable !== null) {
277+
$str .= Context::escape($this->explainedTable);
278+
}
279+
280+
if ($this->explainedColumn !== null) {
281+
$str .= ' ' . Context::escape($this->explainedColumn);
203282
}
204283

205284
return $str;

src/Tools/ContextGenerator.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,16 @@ public static function buildAll($input, $output)
398398

399399
foreach ($files as $file) {
400400
// Skipping current and parent directories.
401+
// Skipping _functions* and _common.txt files
401402
if (($file[0] === '.') || ($file[0] === '_')) {
402403
continue;
403404
}
404405

406+
// Skipping README.md
407+
if ($file === 'README.md') {
408+
continue;
409+
}
410+
405411
// Building the context.
406412
echo sprintf("Building context for %s...\n", $file);
407413
static::build($input . '/' . $file, $output);

tests/Builder/ExplainStatementTest.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public function testBuilder(): void
5252
$parser = new Parser($query);
5353
$stmt = $parser->statements[0];
5454
$this->assertEquals(
55-
'DESCRIBE tablename',
55+
'DESCRIBE `tablename`',
5656
$stmt->build()
5757
);
5858

@@ -65,13 +65,40 @@ public function testBuilder(): void
6565
$stmt->build()
6666
);
6767

68-
/* Assertion 6 */
68+
/* Assertion 7 */
6969
$query = 'EXPLAIN FORMAT=TREE SELECT * FROM db;';
7070
$parser = new Parser($query);
7171
$stmt = $parser->statements[0];
7272
$this->assertEquals(
7373
'EXPLAIN FORMAT=TREE SELECT * FROM db',
7474
$stmt->build()
7575
);
76+
77+
/* Assertion 8 */
78+
$query = 'DESCRIBE tablename colname;';
79+
$parser = new Parser($query);
80+
$stmt = $parser->statements[0];
81+
$this->assertEquals(
82+
'DESCRIBE `tablename` `colname`',
83+
$stmt->build()
84+
);
85+
86+
/* Assertion 9 */
87+
$query = 'DESCRIBE tablename \'col%me\';';
88+
$parser = new Parser($query);
89+
$stmt = $parser->statements[0];
90+
$this->assertEquals(
91+
'DESCRIBE `tablename` `col%me`',
92+
$stmt->build()
93+
);
94+
95+
/* Assertion 9 */
96+
$query = 'DESCRIBE db.tablename \'col%me\';';
97+
$parser = new Parser($query);
98+
$stmt = $parser->statements[0];
99+
$this->assertEquals(
100+
'DESCRIBE `db`.`tablename` `col%me`',
101+
$stmt->build()
102+
);
76103
}
77104
}

tests/Parser/CreateStatementTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public static function createProvider(): array
5252
['parser/parseCreateTable15'],
5353
['parser/parseCreateTable16'],
5454
['parser/parseCreateTable17'],
55+
['parser/parseCreateTable18'],
5556
['parser/parseCreateTableErr1'],
5657
['parser/parseCreateTableErr2'],
5758
['parser/parseCreateTableErr3'],

0 commit comments

Comments
 (0)