diff --git a/packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php b/packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php index 391a1233e..813455340 100644 --- a/packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php +++ b/packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php @@ -2925,14 +2925,22 @@ private function execute_create_table_statement( WP_Parser_Node $node ): void { // Handle IF NOT EXISTS. if ( $subnode->has_child_node( 'ifNotExists' ) ) { - $tables_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'tables' ); - $table_exists = $this->execute_sqlite_query( - sprintf( - 'SELECT 1 FROM %s WHERE table_schema = ? AND table_name = ?', - $this->quote_sqlite_identifier( $tables_table ) - ), - array( $this->get_saved_db_name(), $table_name ) - )->fetchColumn(); + if ( $table_is_temporary ) { + /* + * The temporary information schema tables are created with the + * first temporary table of a session, so they may not exist yet. + */ + $table_exists = $this->information_schema_builder->temporary_table_exists( $table_name ); + } else { + $tables_table = $this->information_schema_builder->get_table_name( false, 'tables' ); + $table_exists = $this->execute_sqlite_query( + sprintf( + 'SELECT 1 FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $tables_table ) + ), + array( $this->get_saved_db_name(), $table_name ) + )->fetchColumn(); + } if ( $table_exists ) { $this->last_result_statement = $this->create_result_statement_from_data( array(), array() ); diff --git a/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php b/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php index f4770b21e..bbd3728fb 100644 --- a/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php +++ b/packages/mysql-on-sqlite/src/sqlite/class-wp-sqlite-information-schema-builder.php @@ -781,7 +781,16 @@ public function record_drop_table( WP_Parser_Node $node ): void { $table_refs = $child_node->get_first_child_node( 'tableRefList' )->get_child_nodes(); foreach ( $table_refs as $table_ref ) { $table_name = $this->get_table_name_from_node( $table_ref ); - $table_is_temporary = $has_temporary_keyword || $this->temporary_table_exists( $table_name ); + $table_is_temporary = $this->temporary_table_exists( $table_name ); + + /* + * DROP TEMPORARY TABLE drops only temporary tables. When there is no + * such table, there is nothing to record. The temporary information + * schema tables may not even exist yet. + */ + if ( $has_temporary_keyword && ! $table_is_temporary ) { + continue; + } $this->delete_values( $this->get_table_name( $table_is_temporary, 'tables' ), diff --git a/packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php b/packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php index b81d767ec..8a9446781 100644 --- a/packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php +++ b/packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php @@ -4433,6 +4433,68 @@ public function testCreateTemporaryTableIfNotExists(): void { ); } + public function testCreateTemporaryTableIfNotExistsAsFirstTemporaryTable(): void { + // No temporary table exists in this session yet. + $this->assertQuery( + 'CREATE TEMPORARY TABLE IF NOT EXISTS t (ID INTEGER, name TEXT)' + ); + $this->assertQuery( "INSERT INTO t (ID, name) VALUES (1, 'a')" ); + + // A repeated statement is a no-op and keeps the existing data, + // also when the table name is cased differently. + $this->assertQuery( + 'CREATE TEMPORARY TABLE IF NOT EXISTS t (ID INTEGER, name TEXT)' + ); + $this->assertQuery( + 'CREATE TEMPORARY TABLE IF NOT EXISTS T (ID INTEGER, name TEXT)' + ); + $result = $this->assertQuery( 'SELECT ID, name FROM t' ); + $this->assertEquals( + array( + (object) array( + 'ID' => '1', + 'name' => 'a', + ), + ), + $result + ); + } + + public function testCreateTemporaryTableIfNotExistsWithStandardTableOfSameName(): void { + $this->assertQuery( 'CREATE TABLE t (a INT)' ); + + // The standard table doesn't prevent creating the temporary table. + $this->assertQuery( 'CREATE TEMPORARY TABLE IF NOT EXISTS t (b INT)' ); + $result = $this->assertQuery( 'SHOW COLUMNS FROM t' ); + $this->assertCount( 1, $result ); + $this->assertEquals( 'b', $result[0]->Field ); + + // The standard table is unchanged. + $this->assertQuery( 'DROP TEMPORARY TABLE t' ); + $result = $this->assertQuery( 'SHOW COLUMNS FROM t' ); + $this->assertCount( 1, $result ); + $this->assertEquals( 'a', $result[0]->Field ); + } + + public function testDropTemporaryTableIfExistsAsFirstTemporaryTable(): void { + // No temporary table exists in this session yet. + $this->assertQuery( 'DROP TEMPORARY TABLE IF EXISTS t' ); + + // A standard table with the same name is not dropped. + $this->assertQuery( 'CREATE TABLE t (a INT)' ); + $this->assertQuery( 'DROP TEMPORARY TABLE IF EXISTS t' ); + $result = $this->assertQuery( 'SHOW COLUMNS FROM t' ); + $this->assertCount( 1, $result ); + $this->assertEquals( 'a', $result[0]->Field ); + } + + public function testDropTemporaryTableAsFirstTemporaryTable(): void { + // No temporary table exists in this session yet. + $this->expectException( WP_MySQL_On_SQLite_Exception::class ); + $this->expectExceptionMessage( "Table 'temp.t' doesn't exist" ); + $this->assertQuery( 'DROP TEMPORARY TABLE t' ); + } + public function testTranslatesComplexDelete() { $this->sqlite->query( "CREATE TABLE wptests_dummy (