Skip to content

Commit a5a74e7

Browse files
committed
Merge branch '5.10.x' into 5.11.x
Signed-off-by: Maurício Meneghini Fauth <[email protected]>
2 parents 91d980a + bc9ce73 commit a5a74e7

19 files changed

+1318
-64
lines changed

psalm-baseline.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@
671671
<code>$this-&gt;last</code>
672672
<code>$this-&gt;last</code>
673673
</LoopInvalidation>
674-
<MixedArrayAccess occurrences="41">
674+
<MixedArrayAccess occurrences="43">
675675
<code>$this-&gt;str[$this-&gt;last + 1]</code>
676676
<code>$this-&gt;str[$this-&gt;last++]</code>
677677
<code>$this-&gt;str[$this-&gt;last]</code>
@@ -713,6 +713,8 @@
713713
<code>$this-&gt;str[$this-&gt;last]</code>
714714
<code>$this-&gt;str[$this-&gt;last]</code>
715715
<code>$this-&gt;str[$this-&gt;last]</code>
716+
<code>$this-&gt;str[$this-&gt;last]</code>
717+
<code>$this-&gt;str[$this-&gt;last]</code>
716718
</MixedArrayAccess>
717719
<MixedAssignment occurrences="2">
718720
<code>$lastToken</code>

src/Components/AlterOperation.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ class AlterOperation extends Component
131131
'BY' => 2,
132132
'FOREIGN' => 2,
133133
'FULLTEXT' => 2,
134-
'KEY' => 2,
134+
'KEY' => [
135+
2,
136+
'var',
137+
],
135138
'KEYS' => 2,
136139
'PARTITION' => 2,
137140
'PARTITION BY' => 2,

src/Lexer.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ public function parseNumber()
812812
// 1 --------------------[ + or - ]-------------------> 1
813813
// 1 -------------------[ 0x or 0X ]------------------> 2
814814
// 1 --------------------[ 0 to 9 ]-------------------> 3
815-
// 1 -----------------------[ . ]---------------------> 4
815+
// 1 -----------------------[ . ]---------------------> 10
816816
// 1 -----------------------[ b ]---------------------> 7
817817
//
818818
// 2 --------------------[ 0 to F ]-------------------> 2
@@ -831,11 +831,16 @@ public function parseNumber()
831831
// 8 --------------------[ 0 or 1 ]-------------------> 8
832832
// 8 -----------------------[ ' ]---------------------> 9
833833
//
834+
// 10 -------------------[ 0 to 9 ]-------------------> 4
835+
//
834836
// State 1 may be reached by negative numbers.
835837
// State 2 is reached only by hex numbers.
836838
// State 4 is reached only by float numbers.
837839
// State 5 is reached only by numbers in approximate form.
838840
// State 7 is reached only by numbers in bit representation.
841+
// State 10 is a forced proxy to state 4 ensuring a starting dot (= "0.something") precedes a digit, and not "e"
842+
// or "E" causing wrongly interpreted scientific notation (".e[0 to 9]" is invalid). Such invalid notation could
843+
// break the lexer when table names under a given database context starts with ".e[0-9]".
839844
//
840845
// Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a
841846
// state other than these is invalid.
@@ -858,7 +863,7 @@ public function parseNumber()
858863
} elseif ($this->str[$this->last] >= '0' && $this->str[$this->last] <= '9') {
859864
$state = 3;
860865
} elseif ($this->str[$this->last] === '.') {
861-
$state = 4;
866+
$state = 10;
862867
} elseif ($this->str[$this->last] === 'b') {
863868
$state = 7;
864869
} elseif ($this->str[$this->last] !== '+') {
@@ -885,7 +890,7 @@ public function parseNumber()
885890
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
886891
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
887892
) {
888-
// A number can't be directly followed by a letter
893+
// A number can't be directly followed by a letter
889894
$state = -$state;
890895
} elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
891896
// Just digits and `.`, `e` and `E` are valid characters.
@@ -899,7 +904,7 @@ public function parseNumber()
899904
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
900905
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
901906
) {
902-
// A number can't be directly followed by a letter
907+
// A number can't be directly followed by a letter
903908
$state = -$state;
904909
} elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
905910
// Just digits, `e` and `E` are valid characters.
@@ -916,7 +921,7 @@ public function parseNumber()
916921
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
917922
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
918923
) {
919-
// A number can't be directly followed by a letter
924+
// A number can't be directly followed by a letter
920925
$state = -$state;
921926
} else {
922927
break;
@@ -941,6 +946,13 @@ public function parseNumber()
941946
}
942947
} elseif ($state === 9) {
943948
break;
949+
} elseif ($state === 10) {
950+
$flags |= Token::FLAG_NUMBER_FLOAT;
951+
if ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
952+
break;
953+
}
954+
955+
$state = 4;
944956
}
945957

946958
$token .= $this->str[$this->last];

src/Statements/SelectStatement.php

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

77
use PhpMyAdmin\SqlParser\Components\ArrayObj;
8+
use PhpMyAdmin\SqlParser\Components\CaseExpression;
89
use PhpMyAdmin\SqlParser\Components\Condition;
910
use PhpMyAdmin\SqlParser\Components\Expression;
1011
use PhpMyAdmin\SqlParser\Components\FunctionCall;
@@ -234,7 +235,7 @@ class SelectStatement extends Statement
234235
/**
235236
* Expressions that are being selected by this statement.
236237
*
237-
* @var Expression[]
238+
* @var (CaseExpression|Expression)[]
238239
*/
239240
public $expr = [];
240241

src/TokensList.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use ArrayAccess;
88

9+
use function array_splice;
910
use function count;
1011
use function in_array;
1112
use function is_array;
@@ -213,25 +214,29 @@ public function getNextOfTypeAndFlag(int $type, int $flag): ?Token
213214
}
214215

215216
/**
216-
* Sets an value inside the container.
217+
* Sets a Token inside the list of tokens.
218+
* When defined, offset must be positive otherwise the offset is ignored.
219+
* If the offset is not defined (like in array_push) or if it is greater than the number of Tokens already stored,
220+
* the Token is appended to the list of tokens.
217221
*
218-
* @param int|null $offset the offset to be set
222+
* @param int|null $offset the offset to be set. Must be positive otherwise, nothing will be stored.
219223
* @param Token $value the token to be saved
220224
*
221225
* @return void
222226
*/
223227
#[\ReturnTypeWillChange]
224228
public function offsetSet($offset, $value)
225229
{
226-
if ($offset === null) {
230+
if ($offset === null || $offset >= $this->count) {
227231
$this->tokens[$this->count++] = $value;
228-
} else {
232+
} elseif ($offset >= 0) {
229233
$this->tokens[$offset] = $value;
230234
}
231235
}
232236

233237
/**
234-
* Gets a value from the container.
238+
* Gets a Token from the list of tokens.
239+
* If the offset is negative or above the number of tokens set in the list, will return null.
235240
*
236241
* @param int $offset the offset to be returned
237242
*
@@ -240,11 +245,12 @@ public function offsetSet($offset, $value)
240245
#[\ReturnTypeWillChange]
241246
public function offsetGet($offset)
242247
{
243-
return $offset < $this->count ? $this->tokens[$offset] : null;
248+
return $this->offsetExists($offset) ? $this->tokens[$offset] : null;
244249
}
245250

246251
/**
247252
* Checks if an offset was previously set.
253+
* If the offset is negative or above the number of tokens set in the list, will return false.
248254
*
249255
* @param int $offset the offset to be checked
250256
*
@@ -253,11 +259,11 @@ public function offsetGet($offset)
253259
#[\ReturnTypeWillChange]
254260
public function offsetExists($offset)
255261
{
256-
return $offset < $this->count;
262+
return $offset >= 0 && $offset < $this->count;
257263
}
258264

259265
/**
260-
* Unsets the value of an offset.
266+
* Unsets the value of an offset, if the offset exists.
261267
*
262268
* @param int $offset the offset to be unset
263269
*
@@ -266,12 +272,11 @@ public function offsetExists($offset)
266272
#[\ReturnTypeWillChange]
267273
public function offsetUnset($offset)
268274
{
269-
unset($this->tokens[$offset]);
270-
--$this->count;
271-
for ($i = $offset; $i < $this->count; ++$i) {
272-
$this->tokens[$i] = $this->tokens[$i + 1];
275+
if (! $this->offsetExists($offset)) {
276+
return;
273277
}
274278

275-
unset($this->tokens[$this->count]);
279+
array_splice($this->tokens, $offset, 1);
280+
--$this->count;
276281
}
277282
}

tests/Lexer/TokensListTest.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,41 @@ public function testArrayAccess(): void
137137
// offsetSet($offset, $value)
138138
$list[2] = $this->tokens[2];
139139

140+
// offsetSet($offset, $value) with overflowed offset
141+
$list[$list->count] = $this->tokens[1];
142+
$this->assertSame($list[$list->count - 1], $this->tokens[1]);
143+
$this->assertSame($list->count, count($this->tokens) + 1);
144+
145+
// offsetGet($offset) with negative offset
146+
$this->assertNull($list[-1]);
147+
148+
// offsetGet($offset) with overflow offset
149+
$this->assertNull($list[$list->count]);
150+
140151
// offsetGet($offset)
141152
for ($i = 0, $count = count($this->tokens); $i < $count; ++$i) {
142-
$this->assertEquals($this->tokens[$i], $list[$i]);
153+
$this->assertSame($this->tokens[$i], $list[$i]);
143154
}
144155

145156
// offsetExists($offset)
146157
$this->assertArrayHasKey(2, $list);
147-
$this->assertArrayNotHasKey(13, $list);
158+
$this->assertArrayNotHasKey(14, $list);
159+
$this->assertArrayNotHasKey(-8, $list);
148160

149161
// offsetUnset($offset)
162+
$currentCountTokens = $list->count;
150163
unset($list[2]);
151-
$this->assertEquals($this->tokens[3], $list[2]);
164+
$newCountTokens = $list->count;
165+
$this->assertSame($this->tokens[3], $list[2]);
166+
$this->assertSame($currentCountTokens - 1, $newCountTokens);
167+
168+
// offsetUnset($offset) with invalid offset (negative or overflowed)
169+
$currentListTokens = $list->tokens;
170+
unset($list[-1]);
171+
$this->assertSame($currentListTokens, $list->tokens);
172+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
173+
unset($list[999]);
174+
$this->assertSame($currentListTokens, $list->tokens);
175+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
152176
}
153177
}

tests/Parser/AlterStatementTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public static function alterProvider(): array
4242
['parser/parseAlterErr4'],
4343
['parser/parseAlterTableRenameIndex1'],
4444
['parser/parseAlterTableRenameIndex2'],
45+
['parser/parseAlterTableRenameKey1'],
46+
['parser/parseAlterTableRenameKey2'],
4547
['parser/parseAlterTablePartitionByRange1'],
4648
['parser/parseAlterTablePartitionByRange2'],
4749
['parser/parseAlterTableCoalescePartition'],

tests/Parser/LoadStatementTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public static function loadProvider(): array
3838
['parser/parseLoad5'],
3939
['parser/parseLoad6'],
4040
['parser/parseLoad7'],
41+
['parser/parseLoad8'],
4142
['parser/parseLoadErr1'],
4243
['parser/parseLoadErr2'],
4344
['parser/parseLoadErr3'],

tests/data/bugs/gh317.out

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -209,19 +209,15 @@
209209
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
210210
"options": {
211211
"1": "ADD",
212-
"2": "KEY"
212+
"2": {
213+
"name": "KEY",
214+
"equals": false,
215+
"expr": "`IDX_REPAIR`",
216+
"value": "IDX_REPAIR"
217+
}
213218
}
214219
},
215-
"field": {
216-
"@type": "PhpMyAdmin\\SqlParser\\Components\\Expression",
217-
"database": null,
218-
"table": null,
219-
"column": "IDX_REPAIR",
220-
"expr": "`IDX_REPAIR`",
221-
"alias": null,
222-
"function": null,
223-
"subquery": null
224-
},
220+
"field": null,
225221
"partitions": null,
226222
"unknown": [
227223
{

tests/data/lexer/lexNumber.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, +0xfFA, -0xFFa, -0xfFA, 1e-10, 1e10, .5e10, b'10';
22
-- invalid numbers
3-
SELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA;
3+
SELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA, .e4;

tests/data/lexer/lexNumber.out

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"query": "SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, +0xfFA, -0xFFa, -0xfFA, 1e-10, 1e10, .5e10, b'10';\n-- invalid numbers\nSELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA;",
2+
"query": "SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, +0xfFA, -0xFFa, -0xfFA, 1e-10, 1e10, .5e10, b'10';\n-- invalid numbers\nSELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA, .e4;\n",
33
"lexer": {
44
"@type": "PhpMyAdmin\\SqlParser\\Lexer",
5-
"str": "SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, +0xfFA, -0xFFa, -0xfFA, 1e-10, 1e10, .5e10, b'10';\n-- invalid numbers\nSELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA;",
6-
"len": 176,
7-
"last": 176,
5+
"str": "SELECT 12, 34, 5.67, 0x89, -10, --11, +12, .15, 0xFFa, 0xfFA, +0xfFA, -0xFFa, -0xfFA, 1e-10, 1e10, .5e10, b'10';\n-- invalid numbers\nSELECT 12ex10, b'15', 0XFfA, -0XFfA, +0XFfA, .e4;\n",
6+
"len": 182,
7+
"last": 182,
88
"list": {
99
"@type": "PhpMyAdmin\\SqlParser\\TokensList",
1010
"tokens": [
@@ -665,14 +665,59 @@
665665
"flags": 0,
666666
"position": 170
667667
},
668+
{
669+
"@type": "PhpMyAdmin\\SqlParser\\Token",
670+
"token": ",",
671+
"value": ",",
672+
"keyword": null,
673+
"type": 2,
674+
"flags": 16,
675+
"position": 175
676+
},
677+
{
678+
"@type": "PhpMyAdmin\\SqlParser\\Token",
679+
"token": " ",
680+
"value": " ",
681+
"keyword": null,
682+
"type": 3,
683+
"flags": 0,
684+
"position": 176
685+
},
686+
{
687+
"@type": "PhpMyAdmin\\SqlParser\\Token",
688+
"token": ".",
689+
"value": ".",
690+
"keyword": null,
691+
"type": 2,
692+
"flags": 16,
693+
"position": 177
694+
},
695+
{
696+
"@type": "PhpMyAdmin\\SqlParser\\Token",
697+
"token": "e4",
698+
"value": "e4",
699+
"keyword": null,
700+
"type": 0,
701+
"flags": 0,
702+
"position": 178
703+
},
668704
{
669705
"@type": "PhpMyAdmin\\SqlParser\\Token",
670706
"token": ";",
671707
"value": ";",
672708
"keyword": null,
673709
"type": 9,
674710
"flags": 0,
675-
"position": 175
711+
"position": 180
712+
},
713+
{
714+
"@type": "PhpMyAdmin\\SqlParser\\Token",
715+
"token": "\n",
716+
"value": " ",
717+
"keyword": null,
718+
"type": 3,
719+
"flags": 0,
720+
"position": 181
676721
},
677722
{
678723
"@type": "PhpMyAdmin\\SqlParser\\Token",
@@ -684,7 +729,7 @@
684729
"position": null
685730
}
686731
],
687-
"count": 75,
732+
"count": 80,
688733
"idx": 0
689734
},
690735
"delimiter": ";",

0 commit comments

Comments
 (0)