Skip to content

Commit 26b7d61

Browse files
committed
Fix hex string literal (x'...') parsing in CREATE TABLE DEFAULT values
Signed-off-by: Nicolai Ehrhardt <[email protected]>
1 parent 4c45eac commit 26b7d61

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

src/Lexer.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,11 +833,17 @@ public function parseNumber()
833833
//
834834
// 10 -------------------[ 0 to 9 ]-------------------> 4
835835
//
836+
// 11 ----------------------[ ' ]---------------------> 12
837+
//
838+
// 12 --------[ 0 to 9, A to F, a to f ]--------------> 12
839+
// 12 ----------------------[ ' ]---------------------> 13
840+
//
836841
// State 1 may be reached by negative numbers.
837842
// State 2 is reached only by hex numbers.
838843
// State 4 is reached only by float numbers.
839844
// State 5 is reached only by numbers in approximate form.
840845
// State 7 is reached only by numbers in bit representation.
846+
// State 11 is reached only by hex string literals (x'...').
841847
// State 10 is a forced proxy to state 4 ensuring a starting dot (= "0.something") precedes a digit, and not "e"
842848
// or "E" causing wrongly interpreted scientific notation (".e[0 to 9]" is invalid). Such invalid notation could
843849
// break the lexer when table names under a given database context starts with ".e[0-9]".
@@ -866,6 +872,8 @@ public function parseNumber()
866872
$state = 10;
867873
} elseif ($this->str[$this->last] === 'b') {
868874
$state = 7;
875+
} elseif ($this->str[$this->last] === 'x' || $this->str[$this->last] === 'X') {
876+
$state = 11;
869877
} elseif ($this->str[$this->last] !== '+') {
870878
// `+` is a valid character in a number.
871879
break;
@@ -949,6 +957,29 @@ public function parseNumber()
949957
}
950958
} elseif ($state === 9) {
951959
break;
960+
} elseif ($state === 11) {
961+
// x'...' hex string literal - expect opening quote
962+
$flags |= Token::FLAG_NUMBER_HEX_STRING;
963+
if ($this->str[$this->last] !== '\'') {
964+
break;
965+
}
966+
967+
$state = 12;
968+
} elseif ($state === 12) {
969+
// x'...' hex string literal - hex digits or closing quote
970+
if ($this->str[$this->last] === '\'') {
971+
$state = 13;
972+
} elseif (
973+
! (
974+
($this->str[$this->last] >= '0' && $this->str[$this->last] <= '9')
975+
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'F')
976+
|| ($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'f')
977+
)
978+
) {
979+
break;
980+
}
981+
} elseif ($state === 13) {
982+
break;
952983
} elseif ($state === 10) {
953984
$flags |= Token::FLAG_NUMBER_FLOAT;
954985
if ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
@@ -961,7 +992,7 @@ public function parseNumber()
961992
$token .= $this->str[$this->last];
962993
}
963994

964-
if ($state === 2 || $state === 3 || ($token !== '.' && $state === 4) || $state === 6 || $state === 9) {
995+
if ($state === 2 || $state === 3 || ($token !== '.' && $state === 4) || $state === 6 || $state === 9 || $state === 13) {
965996
--$this->last;
966997

967998
return new Token($token, Token::TYPE_NUMBER, $flags);

src/Token.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class Token
142142
public const FLAG_NUMBER_APPROXIMATE = 4;
143143
public const FLAG_NUMBER_NEGATIVE = 8;
144144
public const FLAG_NUMBER_BINARY = 16;
145+
public const FLAG_NUMBER_HEX_STRING = 32;
145146

146147
// Strings related flags.
147148
public const FLAG_STRING_SINGLE_QUOTES = 1;
@@ -263,7 +264,7 @@ public function extract()
263264
}
264265
} elseif (($this->flags & self::FLAG_NUMBER_APPROXIMATE) || ($this->flags & self::FLAG_NUMBER_FLOAT)) {
265266
$ret = (float) $ret;
266-
} elseif (! ($this->flags & self::FLAG_NUMBER_BINARY)) {
267+
} elseif (! ($this->flags & self::FLAG_NUMBER_BINARY) && ! ($this->flags & self::FLAG_NUMBER_HEX_STRING)) {
267268
$ret = (int) $ret;
268269
}
269270

tests/Builder/CreateStatementTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,4 +871,26 @@ public function testBuildCreateTableComplexIndexes(): void
871871
$stmt->build()
872872
);
873873
}
874+
875+
public function testBuildCreateTableWithHexDefault(): void
876+
{
877+
$sql = "CREATE TABLE `test` (\n"
878+
. " `IP` binary(16) NOT NULL DEFAULT x'00000000000000000000000000000000'\n"
879+
. ') ENGINE=InnoDB';
880+
881+
$parser = new Parser($sql);
882+
$this->assertEmpty($parser->errors);
883+
$this->assertEquals($sql, $parser->statements[0]->build());
884+
}
885+
886+
public function testBuildCreateTableWithUppercaseHexDefault(): void
887+
{
888+
$sql = "CREATE TABLE `test` (\n"
889+
. " `IP` binary(16) NOT NULL DEFAULT X'FF'\n"
890+
. ')';
891+
892+
$parser = new Parser($sql);
893+
$this->assertEmpty($parser->errors);
894+
$this->assertStringContainsString("DEFAULT X'FF'", $parser->statements[0]->build());
895+
}
874896
}

0 commit comments

Comments
 (0)