PluginProbe
SQLite Database Integration / 2.2.22
SQLite Database Integration v2.2.22
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
sqlite-database-integration / wp-includes / database / sqlite / class-wp-sqlite-configurator.php

class-wp-sqlite-configurator.php in SQLite Database Integration 2.2.22, at wp-includes/database/sqlite/class-wp-sqlite-configurator.php

274 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * SQLite database configurator.
5 *
6 * This class initializes and configures the SQLite database, so that it can be
7 * used by the SQLite driver to translate and emulate MySQL queries in SQLite.
8 *
9 * The configurator ensures that tables required for emulating MySQL behaviors
10 * are created and populated with necessary data. It is also able to partially
11 * repair and update these tables and metadata in case of database corruption.
12 */
13 class WP_SQLite_Configurator {
14 /**
15 * The SQLite driver instance.
16 *
17 * @var WP_PDO_MySQL_On_SQLite
18 */
19 private $driver;
20
21 /**
22 * A service for managing MySQL INFORMATION_SCHEMA tables in SQLite.
23 *
24 * @var WP_SQLite_Information_Schema_Builder
25 */
26 private $schema_builder;
27
28 /**
29 * A service for reconstructing the MySQL INFORMATION_SCHEMA tables in SQLite.
30 *
31 * @var WP_SQLite_Information_Schema_Reconstructor
32 */
33 private $schema_reconstructor;
34
35 /**
36 * Constructor.
37 *
38 * @param WP_PDO_MySQL_On_SQLite $driver The SQLite driver instance.
39 * @param WP_SQLite_Information_Schema_Builder $schema_builder The information schema builder instance.
40 */
41 public function __construct(
42 WP_PDO_MySQL_On_SQLite $driver,
43 WP_SQLite_Information_Schema_Builder $schema_builder
44 ) {
45 $this->driver = $driver;
46 $this->schema_builder = $schema_builder;
47 $this->schema_reconstructor = new WP_SQLite_Information_Schema_Reconstructor(
48 $driver,
49 $schema_builder
50 );
51 }
52
53 /**
54 * Ensure that the SQLite database is configured.
55 *
56 * This method checks if the database is configured for the latest SQLite
57 * driver version, and if it is not, it will configure the database.
58 */
59 public function ensure_database_configured(): void {
60 $version = SQLITE_DRIVER_VERSION;
61 $db_version = $this->driver->get_saved_driver_version();
62 if ( version_compare( $version, $db_version ) > 0 ) {
63 $this->configure_database();
64 }
65 }
66
67 /**
68 * Configure the SQLite database.
69 *
70 * This method creates tables used for emulating MySQL behaviors in SQLite,
71 * and populates them with necessary data. When it is used with an already
72 * configured database, it will update the configuration as per the current
73 * SQLite driver version and attempt to repair any configuration corruption.
74 */
75 public function configure_database(): void {
76 // Use an EXCLUSIVE transaction to prevent multiple connections
77 // from attempting to configure the database at the same time.
78 $this->driver->execute_sqlite_query( 'BEGIN EXCLUSIVE TRANSACTION' );
79 try {
80 $this->ensure_global_variables_table();
81 $this->schema_builder->ensure_information_schema_tables();
82 $this->schema_reconstructor->ensure_correct_information_schema();
83 $this->save_current_driver_version();
84 $this->ensure_database_data();
85 } catch ( Throwable $e ) {
86 $this->driver->execute_sqlite_query( 'ROLLBACK' );
87 throw $e;
88 }
89 $this->driver->execute_sqlite_query( 'COMMIT' );
90 }
91
92 /**
93 * Ensure that the global variables table exists.
94 *
95 * This method configures a database table to store MySQL global variables
96 * and other internal configuration values.
97 */
98 private function ensure_global_variables_table(): void {
99 $this->driver->execute_sqlite_query(
100 sprintf(
101 'CREATE TABLE IF NOT EXISTS %s (name TEXT PRIMARY KEY, value TEXT)',
102 $this->driver->get_connection()->quote_identifier(
103 WP_PDO_MySQL_On_SQLite::GLOBAL_VARIABLES_TABLE_NAME
104 )
105 )
106 );
107 }
108
109 /**
110 * Ensure that the database data is correctly populated.
111 *
112 * This method ensures that the "INFORMATION_SCHEMA.SCHEMATA" table contains
113 * records for both the "INFORMATION_SCHEMA" database and the user database.
114 * 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.
118 */
119 public function ensure_database_data(): void {
120 // Get all databases from the "SCHEMATA" table.
121 $schemata_table = $this->schema_builder->get_table_name( false, 'schemata' );
122 $databases = $this->driver->execute_sqlite_query(
123 sprintf(
124 'SELECT SCHEMA_NAME FROM %s',
125 $this->driver->get_connection()->quote_identifier( $schemata_table )
126 )
127 )->fetchAll( PDO::FETCH_COLUMN ); // phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
128
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 );
139 }
140
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 }
149
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 );
162 }
163
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
231 SET
232 TABLE_SCHEMA = ?,
233 CONSTRAINT_SCHEMA = ?,
234 REFERENCED_TABLE_SCHEMA = CASE WHEN REFERENCED_TABLE_SCHEMA IS NULL THEN NULL ELSE ? END
235 WHERE TABLE_SCHEMA != 'information_schema'",
236 $this->driver->get_connection()->quote_identifier( $key_column_usage_table )
237 ),
238 array( $saved_database_name, $saved_database_name, $saved_database_name )
239 );
240
241 // INFORMATION_SCHEMA.CHECK_CONSTRAINTS
242 $check_constraints_table = $this->schema_builder->get_table_name( false, 'check_constraints' );
243 $this->driver->execute_sqlite_query(
244 sprintf(
245 "UPDATE %s SET CONSTRAINT_SCHEMA = ? WHERE CONSTRAINT_SCHEMA != 'information_schema'",
246 $this->driver->get_connection()->quote_identifier( $check_constraints_table )
247 ),
248 array( $saved_database_name )
249 );
250 }
251 }
252
253 /**
254 * Save the current SQLite driver version.
255 *
256 * This method saves the current SQLite driver version to the database.
257 */
258 private function save_current_driver_version(): void {
259 $this->driver->execute_sqlite_query(
260 sprintf(
261 'INSERT INTO %s (name, value) VALUES (?, ?) ON CONFLICT(name) DO UPDATE SET value = ?',
262 $this->driver->get_connection()->quote_identifier(
263 WP_PDO_MySQL_On_SQLite::GLOBAL_VARIABLES_TABLE_NAME
264 )
265 ),
266 array(
267 WP_PDO_MySQL_On_SQLite::DRIVER_VERSION_VARIABLE_NAME,
268 SQLITE_DRIVER_VERSION,
269 SQLITE_DRIVER_VERSION,
270 )
271 );
272 }
273 }
274