Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

Commit 3830cc3

Browse files
committed
Fix functional/generated default values in non-strict mode
1 parent 25675be commit 3830cc3

File tree

2 files changed

+62
-11
lines changed

2 files changed

+62
-11
lines changed

tests/WP_SQLite_Driver_Tests.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4379,6 +4379,40 @@ public function testNonStrictSqlModeNotNullWithDefault(): void {
43794379
$this->assertSame( '', $result[0]->value );
43804380
}
43814381

4382+
public function testNonStrictModeWithDefaultCurrentTimestamp(): void {
4383+
$this->assertQuery( "SET SESSION sql_mode = ''" );
4384+
$this->assertQuery( 'CREATE TABLE t (id INT, value TIMESTAMP DEFAULT CURRENT_TIMESTAMP)' );
4385+
4386+
// INSERT without a value saves CURRENT_TIMESTAMP:
4387+
$this->assertQuery( 'INSERT INTO t (id) VALUES (1)' );
4388+
$result = $this->assertQuery( 'SELECT * FROM t' );
4389+
$this->assertCount( 1, $result );
4390+
$this->assertRegExp( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $result[0]->value );
4391+
4392+
// UPDATE with NULL saves NULL:
4393+
$this->assertQuery( 'UPDATE t SET value = NULL' );
4394+
$result = $this->assertQuery( 'SELECT * FROM t' );
4395+
$this->assertCount( 1, $result );
4396+
$this->assertNull( $result[0]->value );
4397+
}
4398+
4399+
public function testNonStrictModeWithDefaultCurrentTimestampNotNull(): void {
4400+
$this->assertQuery( "SET SESSION sql_mode = ''" );
4401+
$this->assertQuery( 'CREATE TABLE t (id INT, value TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)' );
4402+
4403+
// INSERT without a value saves CURRENT_TIMESTAMP:
4404+
$this->assertQuery( 'INSERT INTO t (id) VALUES (1)' );
4405+
$result = $this->assertQuery( 'SELECT * FROM t' );
4406+
$this->assertCount( 1, $result );
4407+
$this->assertRegExp( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $result[0]->value );
4408+
4409+
// UPDATE with NULL saves IMPLICIT DEFAULT:
4410+
$this->assertQuery( 'UPDATE t SET value = NULL' );
4411+
$result = $this->assertQuery( 'SELECT * FROM t' );
4412+
$this->assertCount( 1, $result );
4413+
$this->assertSame( '0000-00-00 00:00:00', $result[0]->value );
4414+
}
4415+
43824416
public function testNonStrictSqlModeWithNoListedColumns(): void {
43834417
$this->assertQuery( "SET SESSION sql_mode = ''" );
43844418

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,7 +2973,25 @@ private function translate_insert_or_replace_body_in_non_strict_mode(
29732973
}
29742974
}
29752975

2976-
// 3. Get the list of column names returned by VALUES or SELECT clause.
2976+
// 3. Filter out omitted columns that will get a value from the SQLite engine.
2977+
// That is, nullable columns, columns with defaults, and generated columns.
2978+
$columns = array_values(
2979+
array_filter(
2980+
$columns,
2981+
function ( $column ) use ( $insert_list ) {
2982+
$is_omitted = ! in_array( $column['COLUMN_NAME'], $insert_list, true );
2983+
if ( ! $is_omitted ) {
2984+
return true;
2985+
}
2986+
$is_nullable = 'YES' === $column['IS_NULLABLE'];
2987+
$has_default = $column['COLUMN_DEFAULT'];
2988+
$is_generated = str_contains( $column['EXTRA'], 'auto_increment' );
2989+
return ! ( $is_nullable || $has_default || $is_generated );
2990+
}
2991+
)
2992+
);
2993+
2994+
// 4. Get the list of column names returned by VALUES or SELECT clause.
29772995
$select_list = array();
29782996
if ( 'insertQueryExpression' === $node->rule_name ) {
29792997
// When inserting from a SELECT query, we don't know the column names.
@@ -2994,28 +3012,27 @@ private function translate_insert_or_replace_body_in_non_strict_mode(
29943012
}
29953013
}
29963014

2997-
// 4. Compose a new INSERT field list with all columns from the table.
3015+
// 5. Compose a new INSERT field list with all columns from the table.
29983016
$fragment = '(';
29993017
foreach ( $columns as $i => $column ) {
30003018
$fragment .= $i > 0 ? ', ' : '';
30013019
$fragment .= $this->quote_sqlite_identifier( $column['COLUMN_NAME'] );
30023020
}
30033021
$fragment .= ')';
30043022

3005-
// 5. Compose a wrapper SELECT statement emulating IMPLICIT DEFAULT values.
3023+
// 6. Compose a wrapper SELECT statement emulating IMPLICIT DEFAULT values.
30063024
$fragment .= ' SELECT ';
30073025
foreach ( $columns as $i => $column ) {
30083026
$is_omitted = ! in_array( $column['COLUMN_NAME'], $insert_list, true );
30093027
$fragment .= $i > 0 ? ', ' : '';
30103028
if ( $is_omitted ) {
3011-
// When a column value is omitted from the INSERT statement, we
3012-
// need to use the DEFAULT value or the IMPLICIT DEFAULT value.
3013-
$is_auto_inc = str_contains( $column['EXTRA'], 'auto_increment' );
3014-
$is_nullable = 'YES' === $column['IS_NULLABLE'];
3015-
$default = $column['COLUMN_DEFAULT'];
3016-
if ( null === $default && ! $is_nullable && ! $is_auto_inc ) {
3017-
$default = self::DATA_TYPE_IMPLICIT_DEFAULT_MAP[ $column['DATA_TYPE'] ] ?? null;
3018-
}
3029+
/*
3030+
* When a column is omitted from the INSERT list, we need to use
3031+
* an IMPLICIT DEFAULT value. Note that at this point, all omitted
3032+
* columns that will not get an implicit default are filtered out.
3033+
* (That is, nullable, generated, and columns with true defaults.)
3034+
*/
3035+
$default = self::DATA_TYPE_IMPLICIT_DEFAULT_MAP[ $column['DATA_TYPE'] ] ?? null;
30193036
$fragment .= null === $default ? 'NULL' : $this->connection->quote( $default );
30203037
} else {
30213038
// When a column value is included, we need to apply type casting.

0 commit comments

Comments
 (0)