Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions tests/WP_SQLite_Driver_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -6873,4 +6873,26 @@ public function testUpdateWithJoinComplexQuery(): void {
SET claim_id = 37, last_attempt_gmt = '2025-09-03 12:23:55', last_attempt_local = '2025-09-03 12:23:55'"
);
}

public function testBinaryLiterals(): void {
$result = $this->assertQuery( 'SELECT 0b0100000101111010' );
$this->assertEquals( array( (object) array( '0b0100000101111010' => 'Az' ) ), $result );

$result = $this->assertQuery( "SELECT b'0100000101111010'" );
$this->assertEquals( array( (object) array( "b'0100000101111010'" => 'Az' ) ), $result );

$result = $this->assertQuery( "SELECT B'0100000101111010'" );
$this->assertEquals( array( (object) array( "B'0100000101111010'" => 'Az' ) ), $result );
}

public function testHexadecimalLiterals(): void {
$result = $this->assertQuery( 'SELECT 0x417a' );
$this->assertEquals( array( (object) array( '0x417a' => 'Az' ) ), $result );

$result = $this->assertQuery( "SELECT x'417a'" );
$this->assertEquals( array( (object) array( "x'417a'" => 'Az' ) ), $result );

$result = $this->assertQuery( "SELECT X'417a'" );
$this->assertEquals( array( (object) array( "X'417a'" => 'Az' ) ), $result );
}
}
34 changes: 34 additions & 0 deletions tests/WP_SQLite_Driver_Translation_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,40 @@ public function testSerialDataTypes(): void {
);
}

public function testBinaryLiterals(): void {
// All binary literal syntaxes need to be converted to HEX strings.
$this->assertQuery(
"SELECT x'417a' AS `b'0100000101111010'`",
"SELECT b'0100000101111010'"
);
$this->assertQuery(
"SELECT x'417a' AS `B'0100000101111010'`",
"SELECT B'0100000101111010'"
);
$this->assertQuery(
"SELECT x'417a' AS `0b0100000101111010`",
'SELECT 0b0100000101111010'
);
}

public function testHexadecimalLiterals(): void {
// The x'...' and X'...' syntax should be preserved as is.
$this->assertQuery(
"SELECT x'417a'",
"SELECT x'417a'"
);
$this->assertQuery(
"SELECT X'417a'",
"SELECT X'417a'"
);

// The 0x... syntax needs to be translated to x'...'.
$this->assertQuery(
"SELECT x'417a' AS `0x417a`",
'SELECT 0x417a'
);
}

public function testSystemVariables(): void {
$this->assertQuery(
"SELECT 'ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES' AS `@@sql_mode`",
Expand Down
25 changes: 25 additions & 0 deletions wp-includes/sqlite-ast/class-wp-sqlite-driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2860,6 +2860,31 @@ private function translate_token( WP_MySQL_Token $token ): ?string {
switch ( $token->id ) {
case WP_MySQL_Lexer::EOF:
return null;
case WP_MySQL_Lexer::BIN_NUMBER:
/*
* There are no binary literals in SQLite. We need to convert all
* MySQL binary string values to HEX strings in SQLite (x'...').
*/
$value = $token->get_value();
if ( '0' === $value[0] ) {
// 0b...
$value = substr( $value, 2 );
} else {
// b'...' or B'...'
Copy link
Collaborator

@adamziel adamziel Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also account for X'01', x'01', _binary X'01', and _binary x'01'? Or are these fine because we can just preserve them as they are and we won't see them in this else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamziel X'01' and x'01' will be HEX literals and processed in the case below (HEX_NUMBER). So they should never appear under the WP_MySQL_Lexer::BIN_NUMBER token. These are valid in SQLite, so that case leaves them as is (only 0x... is translated).

For _binary x'01', I think the cast syntax will fail in SQLite at the moment, but behavior-wise, it should be fine, as both x'41' and _binary x'41' represent the same binary bytes — with SELECT, you get A as a result in both cases.

$value = substr( $value, 2, -1 );
}
return sprintf( "x'%s'", base_convert( $value, 2, 16 ) );
case WP_MySQL_Lexer::HEX_NUMBER:
/*
* In MySQL, "0x" prefixed values represent binary literal values,
* while in SQLite, that would be a hexadecimal number. Therefore,
* we need to convert the 0x... syntax to x'...'.
*/
$value = $token->get_value();
if ( '0' === $value[0] && 'x' === $value[1] ) {
return sprintf( "x'%s'", substr( $value, 2 ) );
}
return $value;
case WP_MySQL_Lexer::AUTO_INCREMENT_SYMBOL:
return 'AUTOINCREMENT';
case WP_MySQL_Lexer::BINARY_SYMBOL:
Expand Down
Loading