Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -7747,21 +7747,28 @@ private function create_result_statement_from_data( array $columns, array $rows
* lock. Writing to a connection-private TEMP table uses the temp
* database, which has no shared lock, so it never contends.
*/
$empty_insert = sprintf(
'INSERT INTO %s ( placeholder_for_empty_insert ) SELECT NULL WHERE FALSE',
$this->quote_sqlite_identifier( self::EMPTY_RESULT_TABLE_NAME )
);
if ( ! $this->empty_result_table_ready ) {
$pdo->exec(
sprintf(
'CREATE TEMP TABLE IF NOT EXISTS %s ( placeholder_for_empty_insert )',
$this->quote_sqlite_identifier( self::EMPTY_RESULT_TABLE_NAME )
)
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is becoming kind of complex. I guess we could just always do:

CREATE TEMP TABLE IF NOT EXISTS %s ( placeholder_for_empty_insert );
INSERT INTO %s ( placeholder_for_empty_insert ) SELECT NULL WHERE FALSE;

Or maybe there's a way to make an empty statement without any queries. Checking 馃憖

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is, implemented in #537.

$this->empty_result_table_ready = true;
$this->create_empty_result_table();
}
try {
return $pdo->query( $empty_insert );
} catch ( PDOException $e ) {
/*
* Even though we keep track of whether the table was already
* created, the tracked information may be incorrect when the
* table was created in a transaction that was later rolled back.
* In that case, let's create the table again, and try again.
*/
if ( ! str_contains( $e->getMessage(), 'no such table' ) ) {
throw $e;
}
$this->create_empty_result_table();
return $pdo->query( $empty_insert );
}
return $pdo->query(
sprintf(
'INSERT INTO %s ( placeholder_for_empty_insert ) SELECT NULL WHERE FALSE',
$this->quote_sqlite_identifier( self::EMPTY_RESULT_TABLE_NAME )
)
);
}

/*
Expand Down Expand Up @@ -7815,6 +7822,20 @@ private function create_result_statement_from_data( array $columns, array $rows
return $pdo->query( $query );
}

/**
* Create the TEMP table backing empty result sets on this connection.
* See create_result_statement_from_data().
*/
private function create_empty_result_table(): void {
$this->connection->get_pdo()->exec(
sprintf(
'CREATE TEMP TABLE IF NOT EXISTS %s ( placeholder_for_empty_insert )',
$this->quote_sqlite_identifier( self::EMPTY_RESULT_TABLE_NAME )
)
);
$this->empty_result_table_ready = true;
}

/**
* Create a new MySQL-on-SQLite driver exception.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,29 @@ public function test_rollback(): void {
$this->assertTrue( $result );
}

public function test_rollback_statement_as_first_transaction_on_connection(): void {
// The first empty result set on the connection is created in the transaction.
$this->driver->exec( 'START TRANSACTION' );
$this->driver->exec( 'ROLLBACK' );
$this->assertFalse( $this->driver->inTransaction() );

// Statements with empty result sets keep working after the rollback.
$this->driver->exec( 'START TRANSACTION' );
$this->driver->exec( 'CREATE TABLE t (id INT)' );
$this->driver->exec( 'ROLLBACK' );
$this->assertSame( 0, $this->driver->exec( 'CREATE TABLE t (id INT)' ) );
}

public function test_rollback_method_as_first_transaction_on_connection(): void {
// The first empty result set on the connection is created in the transaction.
$this->driver->beginTransaction();
$this->driver->exec( 'CREATE TABLE t (id INT)' );
$this->assertTrue( $this->driver->rollBack() );

// Statements with empty result sets keep working after the rollback.
$this->assertSame( 0, $this->driver->exec( 'CREATE TABLE t (id INT)' ) );
}

public function test_rollback_no_active_transaction(): void {
$this->expectException( PDOException::class );
$this->expectExceptionMessage( 'There is no active transaction' );
Expand Down
Loading