Skip to content

Commit 8575753

Browse files
committed
Correctly save and migrate database name in information schema tables
1 parent 3f2ffea commit 8575753

2 files changed

Lines changed: 123 additions & 101 deletions

File tree

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

Lines changed: 122 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@
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-
2114
/**
2215
* The SQLite driver instance.
2316
*
@@ -42,16 +35,13 @@ class WP_SQLite_Configurator {
4235
/**
4336
* Constructor.
4437
*
45-
* @param string $db_name The name of the database.
4638
* @param WP_SQLite_Driver $driver The SQLite driver instance.
4739
* @param WP_SQLite_Information_Schema_Builder $schema_builder The information schema builder instance.
4840
*/
4941
public function __construct(
50-
string $db_name,
5142
WP_SQLite_Driver $driver,
5243
WP_SQLite_Information_Schema_Builder $schema_builder
5344
) {
54-
$this->db_name = $db_name;
5545
$this->driver = $driver;
5646
$this->schema_builder = $schema_builder;
5747
$this->schema_reconstructor = new WP_SQLite_Information_Schema_Reconstructor(
@@ -72,20 +62,6 @@ public function ensure_database_configured(): void {
7262
if ( version_compare( $version, $db_version ) > 0 ) {
7363
$this->configure_database();
7464
}
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-
}
8965
}
9066

9167
/**
@@ -105,7 +81,7 @@ public function configure_database(): void {
10581
$this->schema_builder->ensure_information_schema_tables();
10682
$this->schema_reconstructor->ensure_correct_information_schema();
10783
$this->save_current_driver_version();
108-
$this->ensure_schemata_data();
84+
$this->ensure_database_data();
10985
} catch ( Throwable $e ) {
11086
$this->driver->execute_sqlite_query( 'ROLLBACK' );
11187
throw $e;
@@ -131,70 +107,142 @@ private function ensure_global_variables_table(): void {
131107
}
132108

133109
/**
134-
* Ensure that the "SCHEMATA" table data is correctly populated.
110+
* Ensure that the database data is correctly populated.
135111
*
136112
* This method ensures that the "INFORMATION_SCHEMA.SCHEMATA" table contains
137113
* records for both the "INFORMATION_SCHEMA" database and the user database.
138114
* At the moment, only a single user database is supported.
115+
*
116+
* Additionally, this method ensures that the user database name is stored
117+
* correctly in all the information schema tables.
139118
*/
140-
public function ensure_schemata_data(): void {
119+
public function ensure_database_data(): void {
120+
// Get all databases from the "SCHEMATA" table.
141121
$schemata_table = $this->schema_builder->get_table_name( false, 'schemata' );
142-
143-
// 1. Ensure that the "INFORMATION_SCHEMA" database record exists.
144-
$this->driver->execute_sqlite_query(
145-
sprintf(
146-
'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME)
147-
VALUES (?, ?, ?) ON CONFLICT(SCHEMA_NAME) DO NOTHING',
148-
$this->driver->get_connection()->quote_identifier( $schemata_table )
149-
),
150-
// The "INFORMATION_SCHEMA" database stays on "utf8mb3" even in MySQL 8 and 9.
151-
array( 'information_schema', 'utf8mb3', 'utf8mb3_general_ci' )
152-
);
153-
154-
// 2. Bail out if a user database record already exists.
155-
$user_db_record_exists = $this->driver->execute_sqlite_query(
122+
$databases = $this->driver->execute_sqlite_query(
156123
sprintf(
157-
"SELECT COUNT(*) FROM %s WHERE SCHEMA_NAME != 'information_schema'",
124+
'SELECT SCHEMA_NAME FROM %s',
158125
$this->driver->get_connection()->quote_identifier( $schemata_table )
159126
)
160-
)->fetchColumn() > 0;
127+
)->fetchAll( PDO::FETCH_COLUMN ); // phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
161128

162-
if ( $user_db_record_exists ) {
163-
return;
129+
// Ensure that the "INFORMATION_SCHEMA" database record exists.
130+
if ( ! in_array( 'information_schema', $databases, true ) ) {
131+
$this->driver->execute_sqlite_query(
132+
sprintf(
133+
'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME) VALUES (?, ?, ?)',
134+
$this->driver->get_connection()->quote_identifier( $schemata_table )
135+
),
136+
// The "INFORMATION_SCHEMA" database stays on "utf8mb3" even in MySQL 8 and 9.
137+
array( 'information_schema', 'utf8mb3', 'utf8mb3_general_ci' )
138+
);
164139
}
165140

166-
/*
167-
* 3. Migrate from older driver versions without the "SCHEMATA" table.
168-
*
169-
* If a record with an existing database name value is already stored in
170-
* "INFORMATION_SCHEMA.TABLES", we need to use that value. This ensures
171-
* migration from older driver versions without the "SCHEMATA" table.
172-
*/
173-
$information_schema_db_name = $this->driver->execute_sqlite_query(
174-
sprintf(
175-
'SELECT table_schema FROM %s LIMIT 1',
176-
$this->driver->get_connection()->quote_identifier(
177-
$this->schema_builder->get_table_name( false, 'tables' )
178-
)
179-
)
180-
)->fetchColumn();
141+
// Get the existing user database name.
142+
$existing_user_db_name = null;
143+
foreach ( $databases as $database ) {
144+
if ( 'information_schema' !== strtolower( $database ) ) {
145+
$existing_user_db_name = $database;
146+
break;
147+
}
148+
}
181149

182-
if ( false !== $information_schema_db_name ) {
183-
$db_name = $information_schema_db_name;
184-
} else {
185-
$db_name = $this->db_name;
150+
// Ensure that the user database record exists.
151+
if ( null === $existing_user_db_name ) {
152+
$existing_user_db_name = WP_SQLite_Information_Schema_Builder::SAVED_DATABASE_NAME;
153+
$this->driver->execute_sqlite_query(
154+
sprintf(
155+
'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME) VALUES (?, ?, ?)',
156+
$this->driver->get_connection()->quote_identifier( $schemata_table )
157+
),
158+
// @TODO: This should probably be version-dependent.
159+
// Before MySQL 8, the default was different.
160+
array( $existing_user_db_name, 'utf8mb4', 'utf8mb4_0900_ai_ci' )
161+
);
186162
}
187163

188-
// 4. Create a user database record.
189-
$this->driver->execute_sqlite_query(
190-
sprintf(
191-
'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME) VALUES (?, ?, ?)',
192-
$this->driver->get_connection()->quote_identifier( $schemata_table )
193-
),
194-
// @TODO: This should probably be version-dependent.
195-
// Before MySQL 8, the default was different.
196-
array( $db_name, 'utf8mb4', 'utf8mb4_0900_ai_ci' )
197-
);
164+
// Migrate from older versions without dynamic database names.
165+
$saved_database_name = WP_SQLite_Information_Schema_Builder::SAVED_DATABASE_NAME;
166+
if ( $saved_database_name !== $existing_user_db_name ) {
167+
// INFORMATION_SCHEMA.SCHEMATA
168+
$this->driver->execute_sqlite_query(
169+
sprintf(
170+
"UPDATE %s SET SCHEMA_NAME = ? WHERE SCHEMA_NAME != 'information_schema'",
171+
$this->driver->get_connection()->quote_identifier( $schemata_table )
172+
),
173+
array( $saved_database_name )
174+
);
175+
176+
// INFORMATION_SCHEMA.TABLES
177+
$tables_table = $this->schema_builder->get_table_name( false, 'tables' );
178+
$this->driver->execute_sqlite_query(
179+
sprintf(
180+
"UPDATE %s SET TABLE_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
181+
$this->driver->get_connection()->quote_identifier( $tables_table )
182+
),
183+
array( $saved_database_name )
184+
);
185+
186+
// INFORMATION_SCHEMA.COLUMNS
187+
$columns_table = $this->schema_builder->get_table_name( false, 'columns' );
188+
$this->driver->execute_sqlite_query(
189+
sprintf(
190+
"UPDATE %s SET TABLE_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
191+
$this->driver->get_connection()->quote_identifier( $columns_table )
192+
),
193+
array( $saved_database_name )
194+
);
195+
196+
// INFORMATION_SCHEMA.STATISTICS
197+
$statistics_table = $this->schema_builder->get_table_name( false, 'statistics' );
198+
$this->driver->execute_sqlite_query(
199+
sprintf(
200+
"UPDATE %s SET TABLE_SCHEMA = ?, INDEX_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
201+
$this->driver->get_connection()->quote_identifier( $statistics_table )
202+
),
203+
array( $saved_database_name, $saved_database_name )
204+
);
205+
206+
// INFORMATION_SCHEMA.TABLE_CONSTRAINTS
207+
$table_constraints_table = $this->schema_builder->get_table_name( false, 'table_constraints' );
208+
$this->driver->execute_sqlite_query(
209+
sprintf(
210+
"UPDATE %s SET TABLE_SCHEMA = ?, CONSTRAINT_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
211+
$this->driver->get_connection()->quote_identifier( $table_constraints_table )
212+
),
213+
array( $saved_database_name, $saved_database_name )
214+
);
215+
216+
// INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
217+
$referential_constraints_table = $this->schema_builder->get_table_name( false, 'referential_constraints' );
218+
$this->driver->execute_sqlite_query(
219+
sprintf(
220+
"UPDATE %s SET CONSTRAINT_SCHEMA = ?, UNIQUE_CONSTRAINT_SCHEMA = ? WHERE CONSTRAINT_SCHEMA != 'information_schema'",
221+
$this->driver->get_connection()->quote_identifier( $referential_constraints_table )
222+
),
223+
array( $saved_database_name, $saved_database_name )
224+
);
225+
226+
// INFORMATION_SCHEMA.KEY_COLUMN_USAGE
227+
$key_column_usage_table = $this->schema_builder->get_table_name( false, 'key_column_usage' );
228+
$this->driver->execute_sqlite_query(
229+
sprintf(
230+
"UPDATE %s SET TABLE_SCHEMA = ?, CONSTRAINT_SCHEMA = ?, REFERENCED_TABLE_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
231+
$this->driver->get_connection()->quote_identifier( $key_column_usage_table )
232+
),
233+
array( $saved_database_name, $saved_database_name, $saved_database_name )
234+
);
235+
236+
// INFORMATION_SCHEMA.CHECK_CONSTRAINTS
237+
$check_constraints_table = $this->schema_builder->get_table_name( false, 'check_constraints' );
238+
$this->driver->execute_sqlite_query(
239+
sprintf(
240+
"UPDATE %s SET CONSTRAINT_SCHEMA = ? WHERE CONSTRAINT_SCHEMA != 'information_schema'",
241+
$this->driver->get_connection()->quote_identifier( $check_constraints_table )
242+
),
243+
array( $saved_database_name )
244+
);
245+
}
198246
}
199247

200248
/**

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

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ public function __construct( WP_SQLite_Connection $connection, string $database
641641
);
642642

643643
// Ensure that the database is configured.
644-
$migrator = new WP_SQLite_Configurator( $this->db_name, $this, $this->information_schema_builder );
644+
$migrator = new WP_SQLite_Configurator( $this, $this->information_schema_builder );
645645
$migrator->ensure_database_configured();
646646

647647
$this->connection->set_query_logger(
@@ -700,32 +700,6 @@ public function get_saved_driver_version(): string {
700700
}
701701
}
702702

703-
/**
704-
* Get the database name saved in the database.
705-
*
706-
* The saved database name represents the database name that was used when
707-
* the database was initialized and configured.
708-
*
709-
* @return string The database name.
710-
* @throws PDOException When the query execution fails.
711-
*/
712-
public function get_saved_database_name(): string {
713-
try {
714-
$schemata_table = $this->information_schema_builder->get_table_name( false, 'schemata' );
715-
return $this->execute_sqlite_query(
716-
sprintf(
717-
'SELECT SCHEMA_NAME FROM %s WHERE SCHEMA_NAME != "information_schema" LIMIT 1',
718-
$this->quote_sqlite_identifier( $schemata_table )
719-
)
720-
)->fetchColumn() ?? '';
721-
} catch ( PDOException $e ) {
722-
if ( str_contains( $e->getMessage(), 'no such table' ) ) {
723-
return '';
724-
}
725-
throw $e;
726-
}
727-
}
728-
729703
/**
730704
* Check if a specific SQL mode is active.
731705
*

0 commit comments

Comments
 (0)