Skip to content

Commit e94d763

Browse files
committed
Add verification that a correct database name is used
1 parent 8e0ac04 commit e94d763

3 files changed

Lines changed: 146 additions & 1 deletion

File tree

tests/WP_SQLite_Driver_Tests.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5975,4 +5975,49 @@ public function testComplexInformationSchemaQueries(): void {
59755975
$this->assertCount( 1, $result );
59765976
$this->assertEquals( $create_table_query, $result[0]->{'Create Table'} );
59775977
}
5978+
5979+
public function testDatabaseNameEmpty(): void {
5980+
$pdo = new PDO( 'sqlite::memory:' );
5981+
$connection = new WP_SQLite_Connection( array( 'pdo' => $pdo ) );
5982+
5983+
$this->expectException( WP_SQLite_Driver_Exception::class );
5984+
$this->expectExceptionMessage( 'The database name cannot be empty.' );
5985+
new WP_SQLite_Driver( $connection, '' );
5986+
}
5987+
5988+
public function testDatabaseNameMismatch(): void {
5989+
$pdo = new PDO( 'sqlite::memory:' );
5990+
$connection = new WP_SQLite_Connection( array( 'pdo' => $pdo ) );
5991+
5992+
// Create a driver with database name 'db-one'.
5993+
new WP_SQLite_Driver( $connection, 'db-one' );
5994+
5995+
// Create another driver with the same name - no errors.
5996+
new WP_SQLite_Driver( $connection, 'db-one' );
5997+
5998+
// Create a driver with a different name - failure.
5999+
$this->expectException( WP_SQLite_Driver_Exception::class );
6000+
$this->expectExceptionMessage( "Incorrect database name. The database was created with name 'db-one', but 'db-two' is used in the current session." );
6001+
new WP_SQLite_Driver( $connection, 'db-two' );
6002+
}
6003+
6004+
public function testDatabaseNameMismatchWithExistingInformationSchema(): void {
6005+
$pdo = new PDO( 'sqlite::memory:' );
6006+
$connection = new WP_SQLite_Connection( array( 'pdo' => $pdo ) );
6007+
6008+
// Create a driver with database name 'db-one'.
6009+
$driver = new WP_SQLite_Driver( $connection, 'db-one' );
6010+
6011+
// Create a table so that there is a record in the information schema.
6012+
$driver->query( 'CREATE TABLE t (id INT)' );
6013+
6014+
// Delete all variables, including driver version and database name.
6015+
$pdo->exec( sprintf( 'DELETE FROM %s', WP_SQLite_Driver::GLOBAL_VARIABLES_TABLE_NAME ) );
6016+
6017+
// Create a driver with a different name - failure.
6018+
// An information schema record with a different database name already exists.
6019+
$this->expectException( WP_SQLite_Driver_Exception::class );
6020+
$this->expectExceptionMessage( "Incorrect database name. The database was created with name 'db-one', but 'db-two' is used in the current session." );
6021+
new WP_SQLite_Driver( $connection, 'db-two' );
6022+
}
59786023
}

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
* repair and update these tables and metadata in case of database corruption.
1212
*/
1313
class WP_SQLite_Configurator {
14+
/**
15+
* The name of the database.
16+
*
17+
* @var string
18+
*/
19+
private $db_name;
20+
1421
/**
1522
* The SQLite driver instance.
1623
*
@@ -35,13 +42,16 @@ class WP_SQLite_Configurator {
3542
/**
3643
* Constructor.
3744
*
45+
* @param string $db_name The name of the database.
3846
* @param WP_SQLite_Driver $driver The SQLite driver instance.
3947
* @param WP_SQLite_Information_Schema_Builder $schema_builder The information schema builder instance.
4048
*/
4149
public function __construct(
50+
string $db_name,
4251
WP_SQLite_Driver $driver,
4352
WP_SQLite_Information_Schema_Builder $schema_builder
4453
) {
54+
$this->db_name = $db_name;
4555
$this->driver = $driver;
4656
$this->schema_builder = $schema_builder;
4757
$this->schema_reconstructor = new WP_SQLite_Information_Schema_Reconstructor(
@@ -62,6 +72,20 @@ public function ensure_database_configured(): void {
6272
if ( version_compare( $version, $db_version ) > 0 ) {
6373
$this->configure_database();
6474
}
75+
76+
// Ensure that the database name used in the current session corresponds
77+
// to the database name that is stored in the information schema tables.
78+
$db_name = $this->driver->get_saved_database_name();
79+
if ( $this->db_name !== $db_name ) {
80+
throw new WP_SQLite_Driver_Exception(
81+
$this->driver,
82+
sprintf(
83+
"Incorrect database name. The database was created with name '%s', but '%s' is used in the current session.",
84+
$db_name,
85+
$this->db_name,
86+
)
87+
);
88+
}
6589
}
6690

6791
/**
@@ -81,6 +105,7 @@ public function configure_database(): void {
81105
$this->schema_builder->ensure_information_schema_tables();
82106
$this->schema_reconstructor->ensure_correct_information_schema();
83107
$this->save_current_driver_version();
108+
$this->save_current_database_name();
84109
} catch ( Throwable $e ) {
85110
$this->driver->execute_sqlite_query( 'ROLLBACK' );
86111
throw $e;
@@ -103,6 +128,43 @@ private function ensure_global_variables_table(): void {
103128
);
104129
}
105130

131+
/**
132+
* Save the current database name.
133+
*
134+
* This method saves the current database name to the database.
135+
*/
136+
public function save_current_database_name(): void {
137+
/*
138+
* If a record with an existing database name value is already stored in
139+
* the information schema, we need to use that value. This ensures correct
140+
* migration from older driver versions without the database name variable.
141+
*/
142+
$information_schema_db_name = $this->driver->execute_sqlite_query(
143+
sprintf(
144+
'SELECT table_schema FROM %s LIMIT 1',
145+
$this->driver->get_connection()->quote_identifier(
146+
$this->schema_builder->get_table_name( false, 'tables' )
147+
)
148+
)
149+
)->fetchColumn();
150+
151+
if ( false !== $information_schema_db_name ) {
152+
$db_name = $information_schema_db_name;
153+
} else {
154+
$db_name = $this->db_name;
155+
}
156+
157+
$this->driver->execute_sqlite_query(
158+
sprintf(
159+
'INSERT INTO %s (name, value) VALUES (?, ?) ON CONFLICT(name) DO UPDATE SET value = ?',
160+
$this->driver->get_connection()->quote_identifier(
161+
WP_SQLite_Driver::GLOBAL_VARIABLES_TABLE_NAME
162+
)
163+
),
164+
array( WP_SQLite_Driver::DATABASE_NAME_VARIABLE_NAME, $db_name )
165+
);
166+
}
167+
106168
/**
107169
* Save the current SQLite driver version.
108170
*

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ class WP_SQLite_Driver {
5151
*/
5252
const DRIVER_VERSION_VARIABLE_NAME = self::RESERVED_PREFIX . 'driver_version';
5353

54+
/**
55+
* The name of the SQLite database name variable.
56+
*
57+
* This internal variable is used to store the name of the SQLite database.
58+
*/
59+
const DATABASE_NAME_VARIABLE_NAME = self::RESERVED_PREFIX . 'database_name';
60+
5461
/**
5562
* A map of MySQL tokens to SQLite data types.
5663
*
@@ -440,6 +447,11 @@ public function __construct( WP_SQLite_Connection $connection, string $database
440447
$this->main_db_name = $database;
441448
$this->db_name = $database;
442449

450+
// Check the database name.
451+
if ( '' === $this->db_name ) {
452+
throw $this->new_driver_exception( 'The database name cannot be empty.' );
453+
}
454+
443455
// Check the SQLite version.
444456
$sqlite_version = $this->get_sqlite_version();
445457
if ( version_compare( $sqlite_version, self::MINIMUM_SQLITE_VERSION, '<' ) ) {
@@ -474,7 +486,7 @@ public function __construct( WP_SQLite_Connection $connection, string $database
474486
);
475487

476488
// Ensure that the database is configured.
477-
$migrator = new WP_SQLite_Configurator( $this, $this->information_schema_builder );
489+
$migrator = new WP_SQLite_Configurator( $this->db_name, $this, $this->information_schema_builder );
478490
$migrator->ensure_database_configured();
479491

480492
$this->connection->set_query_logger(
@@ -533,6 +545,32 @@ public function get_saved_driver_version(): string {
533545
}
534546
}
535547

548+
/**
549+
* Get the database name saved in the database.
550+
*
551+
* The saved database name represents the database name that was used when
552+
* the database was initialized and configured.
553+
*
554+
* @return string The database name.
555+
* @throws PDOException When the query execution fails.
556+
*/
557+
public function get_saved_database_name(): string {
558+
try {
559+
return $this->execute_sqlite_query(
560+
sprintf(
561+
'SELECT value FROM %s WHERE name = ?',
562+
$this->quote_sqlite_identifier( self::GLOBAL_VARIABLES_TABLE_NAME )
563+
),
564+
array( self::DATABASE_NAME_VARIABLE_NAME )
565+
)->fetchColumn() ?? '';
566+
} catch ( PDOException $e ) {
567+
if ( str_contains( $e->getMessage(), 'no such table' ) ) {
568+
return '';
569+
}
570+
throw $e;
571+
}
572+
}
573+
536574
/**
537575
* Check if a specific SQL mode is active.
538576
*

0 commit comments

Comments
 (0)