Skip to content

Commit 488b8db

Browse files
authored
Pad binary string literals to full bytes as required by SQLite HEX notation (#255)
This fixes the following error: ```bash SQLSTATE[HY000]: General error: 1 unrecognized token: "x'1'" ``` For queries like the following: ```sql SELECT 0b1; SELECT 0b01; SELECT 0b001; ... ``` SQLite doesn't have binary string literals, so [we need to convert them to HEX string literals](#245). However, HEX string literals in SQLite must be aligned to full bytes (their length needs to be even) — `x'01'` is valid, while `x'1'` is invalid. In MySQL, binary strings don't require such alignment, and `0b1` is valid (and `SELECT 0b1 = 0b01` returns `true`). Additionally, the `base_convert()` function used in the [original implementation](#245) doesn't preserve existing leading `0` padding. To solve both of these issues, this PR implements the following: 1. Count how many full bytes the original binary string requires. 2. Make sure the resulting HEX string has that number of bytes, using leading `0` padding. For HEX strings, this fix is not needed, as those require to be byte-aligned also in MySQL, so throwing an error for `x'1'` is actually correct.
1 parent 990a4e2 commit 488b8db

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

tests/WP_SQLite_Driver_Tests.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6896,6 +6896,22 @@ public function testBinaryLiterals(): void {
68966896

68976897
$result = $this->assertQuery( "SELECT B'0100000101111010'" );
68986898
$this->assertEquals( array( (object) array( "B'0100000101111010'" => 'Az' ) ), $result );
6899+
6900+
// Verify correct padding (0b1 === 0b01 === 0b001 ... === 0x00000001).
6901+
$result = $this->assertQuery( 'SELECT 0b1' );
6902+
$this->assertEquals( array( (object) array( '0b1' => pack( 'H*', '01' ) ) ), $result );
6903+
6904+
$result = $this->assertQuery( 'SELECT 0b01' );
6905+
$this->assertEquals( array( (object) array( '0b01' => pack( 'H*', '01' ) ) ), $result );
6906+
6907+
$result = $this->assertQuery( 'SELECT 0b001' );
6908+
$this->assertEquals( array( (object) array( '0b001' => pack( 'H*', '01' ) ) ), $result );
6909+
6910+
$result = $this->assertQuery( 'SELECT 0b00000001' );
6911+
$this->assertEquals( array( (object) array( '0b00000001' => pack( 'H*', '01' ) ) ), $result );
6912+
6913+
$result = $this->assertQuery( 'SELECT 0b000000001' );
6914+
$this->assertEquals( array( (object) array( '0b000000001' => pack( 'H*', '0001' ) ) ), $result );
68996915
}
69006916

69016917
public function testHexadecimalLiterals(): void {

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2873,7 +2873,18 @@ private function translate_token( WP_MySQL_Token $token ): ?string {
28732873
// b'...' or B'...'
28742874
$value = substr( $value, 2, -1 );
28752875
}
2876-
return sprintf( "x'%s'", base_convert( $value, 2, 16 ) );
2876+
2877+
// Convert the binary string to HEX.
2878+
$hex = base_convert( $value, 2, 16 );
2879+
2880+
/*
2881+
* The "base_convert()" function doesn't add or preserve padding.
2882+
* Let's compute how many bytes we expect and pad the HEX value
2883+
* to full bytes (SQLite requires HEX strings of even length).
2884+
*/
2885+
$byte_count = (int) ceil( strlen( $value ) / 8 );
2886+
$hex = str_pad( $hex, $byte_count * 2, '0', STR_PAD_LEFT );
2887+
return sprintf( "x'%s'", $hex );
28772888
case WP_MySQL_Lexer::HEX_NUMBER:
28782889
/*
28792890
* In MySQL, "0x" prefixed values represent binary literal values,

0 commit comments

Comments
 (0)