Skip to content

Commit 3cbb647

Browse files
committed
Handle null characters in strings
1 parent b78d0ad commit 3cbb647

File tree

2 files changed

+35
-35
lines changed

2 files changed

+35
-35
lines changed

tests/WP_SQLite_Driver_Tests.php

+9-27
Original file line numberDiff line numberDiff line change
@@ -3622,39 +3622,21 @@ public function testCharLength(): void {
36223622
);
36233623
}
36243624

3625-
public function _testAsdf() {
3625+
public function testNullCharactersInStrings(): void {
36263626
$this->assertQuery(
3627-
'CREATE TABLE t (id INT)'
3627+
'CREATE TABLE t (id INT, name VARCHAR(20))'
36283628
);
36293629
$this->assertQuery(
3630-
'INSERT INTO t (id) VALUES (1)'
3630+
"INSERT INTO t (name) VALUES ('a\0b')"
36313631
);
36323632
$this->assertQuery(
3633-
'SELECT non_existent_column FROM t LIMIT 0'
3633+
'SELECT name FROM t'
36343634
);
3635-
var_dump( $this->engine->executed_sqlite_queries );
3636-
var_dump( $this->engine->get_error_message() );
3637-
$this->assertCount( 1, $this->engine->get_query_results() );
3638-
}
3639-
3640-
public function _testXyz() {
3641-
$this->assertQuery(
3642-
"INSERT INTO wp_actionscheduler_actions ( `hook`, `status`, `scheduled_date_gmt`, `scheduled_date_local`, `schedule`, `group_id`, `priority`, `args` ) SELECT 'action_scheduler/migration_hook', 'pending', '2025-01-28 15:14:01', '2025-01-28 15:14:01', 'O:30:\"ActionScheduler_SimpleSchedule\":2:{s:22:\"\0*\0scheduled_timestamp\";i:1738077241;s:41:\"\0ActionScheduler_SimpleSchedule\0timestamp\";i:1738077241;}', 2, 10, '[]' FROM DUAL WHERE ( SELECT NULL FROM DUAL ) IS NULL"
3643-
);
3644-
}
3645-
3646-
public function testAaa() {
3647-
$this->assertQuery(
3648-
'
3649-
CREATE TABLE t (
3650-
id int(11) unsigned NOT NULL AUTO_INCREMENT,
3651-
name varchar(90) NOT NULL,
3652-
type varchar(90) NOT NULL DEFAULT \'default\',
3653-
description varchar(250) NOT NULL DEFAULT \'\',
3654-
created_at timestamp NULL,
3655-
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
3656-
)
3657-
'
3635+
$this->assertEquals(
3636+
array(
3637+
(object) array( 'name' => "a\0b" ),
3638+
),
3639+
$this->engine->get_query_results()
36583640
);
36593641
}
36603642
}

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

+26-8
Original file line numberDiff line numberDiff line change
@@ -1808,16 +1808,34 @@ private function translate_string_literal( WP_Parser_Node $node ): string {
18081808
}
18091809

18101810
/*
1811-
* 6. Remove null characters.
1811+
* 6. Handle null characters.
18121812
*
1813-
* SQLite doesn't support null characters in strings.
1814-
*/
1815-
$value = str_replace( "\0", '', $value );
1816-
1817-
/*
1818-
* 7. Escape and add quotes.
1813+
* SQLite doesn't fully support null characters (\u0000) in strings.
1814+
* However, it can store them and read them, with some limitations.
1815+
*
1816+
* In PHP, null bytes are often produced by the serialize() function.
1817+
* Removing them would damage the serialized data.
1818+
*
1819+
* There is no way to store null bytes using a string literal, so we
1820+
* need to split the string and concatenate null bytes with its parts.
1821+
* This will convert literals will null bytes to expressions.
1822+
*
1823+
* Alternatively, we could replace string literals with parameters and
1824+
* pass them using prepared statements. However, that's not universally
1825+
* applicable for all string literals (e.g., in default column values).
1826+
*
1827+
* See:
1828+
* https://www.sqlite.org/nulinstr.html
18191829
*/
1820-
return "'" . str_replace( "'", "''", $value ) . "'";
1830+
$parts = array();
1831+
foreach ( explode( "\0", $value ) as $segment ) {
1832+
// Escape and quote each segment.
1833+
$parts[] = "'" . str_replace( "'", "''", $segment ) . "'";
1834+
}
1835+
if ( count( $parts ) > 1 ) {
1836+
return '(' . implode( ' || CHAR(0) || ', $parts ) . ')';
1837+
}
1838+
return $parts[0];
18211839
}
18221840

18231841
private function translate_pure_identifier( WP_Parser_Node $node ): string {

0 commit comments

Comments
 (0)