Skip to content

Commit 64468b7

Browse files
authored
Fix implicit result column name/alias for SELECT item literals with NULL bytes (#314)
Fixes `SELECT` queries that have string literals with `NULL` bytes in them and no column alias. E.g.: ```sql SELECT `a<null-byte>b`; -- result column name should be: 'a' ``` This is because in MySQL: - There can’t be `\0` bytes in identifiers (like column names). The lexer will reject that. - But string literals can have them. - And there is a way to make a string literal “identifier-like” (implicit alias = the result “column name”): `SELECT 'abc\0xyz'` The result column name will be: `abc`
1 parent c087051 commit 64468b7

2 files changed

Lines changed: 15 additions & 0 deletions

File tree

tests/WP_SQLite_Driver_Tests.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6138,6 +6138,14 @@ public function testSelectColumnNames(): void {
61386138
$this->assertQuery( 'CREATE TABLE t (id INT, name VARCHAR(255))' );
61396139
$this->assertQuery( 'INSERT INTO t (id, name) VALUES (1, "John"), (2, "Jane")' );
61406140

6141+
// Literal with a NULL byte (no explicit alias).
6142+
$result = $this->assertQuery( "SELECT 'abc\0def'" );
6143+
$this->assertSame( array( 'abc' ), array_keys( (array) $result[0] ) );
6144+
6145+
// Literal with a NULL byte (with an explicit alias).
6146+
$result = $this->assertQuery( "SELECT 'abc\0def' AS `col1`" );
6147+
$this->assertSame( array( 'col1' ), array_keys( (array) $result[0] ) );
6148+
61416149
// Columns (no explicit alias).
61426150
$result = $this->assertQuery( 'SELECT id, name FROM t' );
61436151
$this->assertSame( array( 'id', 'name' ), array_keys( (array) $result[0] ) );

wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4566,6 +4566,13 @@ public function translate_select_item( WP_Parser_Node $node ): string {
45664566
$is_text_string_literal = $text_string_literal && $item === $this->translate( $text_string_literal );
45674567
if ( $is_text_string_literal ) {
45684568
$alias = $text_string_literal->get_first_child_token()->get_value();
4569+
4570+
// When the literal value contains a NULL byte, MySQL truncates the
4571+
// resulting identifier at the position of the first one of them.
4572+
$fist_null_byte_pos = strpos( $alias, "\0" );
4573+
if ( false !== $fist_null_byte_pos ) {
4574+
$alias = substr( $alias, 0, $fist_null_byte_pos );
4575+
}
45694576
return sprintf( '%s AS %s', $item, $this->quote_sqlite_identifier( $alias ) );
45704577
}
45714578

0 commit comments

Comments
 (0)