PluginProbe
SQLite Database Integration / 3.0.2
SQLite Database Integration v3.0.2
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 3.0.2, at wp-includes/database/sqlite/class-wp-sqlite-configurator.php

276 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 * @access private
14 */
15 class WP_SQLite_Configurator {
16 /**
17 * The SQLite driver instance.
18 *
19 * @var WP_MySQL_On_SQLite
20 */
21 private $driver;
22
23 /**
24 * A service for managing MySQL INFORMATION_SCHEMA tables in SQLite.
25 *
26 * @var WP_SQLite_Information_Schema_Builder
27 */
28 private $schema_builder;
29
30 /**
31 * A service for reconstructing the MySQL INFORMATION_SCHEMA tables in SQLite.
32 *
33 * @var WP_SQLite_Information_Schema_Reconstructor
34 */
35 private $schema_reconstructor;
36
37 /**
38 * Constructor.
39 *
40 * @param WP_MySQL_On_SQLite $driver The SQLite driver instance.
41 * @param WP_SQLite_Information_Schema_Builder $schema_builder The information schema builder instance.
42 */
43 public function __construct(
44 WP_MySQL_On_SQLite $driver,
45 WP_SQLite_Information_Schema_Builder $schema_builder
46 ) {
47 $this->driver = $driver;
48 $this->schema_builder = $schema_builder;
49 $this->schema_reconstructor = new WP_SQLite_Information_Schema_Reconstructor(
50 $driver,
51 $schema_builder
52 );
53 }
54
55 /**
56 * Ensure that the SQLite database is configured.
57 *
58 * This method checks if the database is configured for the latest SQLite
59 * driver version, and if it is not, it will configure the database.
60 */
61 public function ensure_database_configured(): void {
62 $version = SQLITE_DRIVER_VERSION;
63 $db_version = $this->driver->get_saved_driver_version();
64 if ( version_compare( $version, $db_version ) > 0 ) {
65 $this->configure_database();
66 }
67 }
68
69 /**
70 * Configure the SQLite database.
71 *
72 * This method creates tables used for emulating MySQL behaviors in SQLite,
73 * and populates them with necessary data. When it is used with an already
74 * configured database, it will update the configuration as per the current
75 * SQLite driver version and attempt to repair any configuration corruption.
76 */
77 public function configure_database(): void {
78 // Use an EXCLUSIVE transaction to prevent multiple connections
79 // from attempting to configure the database at the same time.
80 $this->driver->execute_sqlite_query( 'BEGIN EXCLUSIVE TRANSACTION' );
81 try {
82 $this->ensure_global_variables_table();
83 $this->schema_builder->ensure_information_schema_tables();
84 $this->schema_reconstructor->ensure_correct_information_schema();
85 $this->save_current_driver_version();
86 $this->ensure_database_data();
87 } catch ( Throwable $e ) {
88 $this->driver->execute_sqlite_query( 'ROLLBACK' );
89 throw $e;
90 }
91 $this->driver->execute_sqlite_query( 'COMMIT' );
92 }
93
94 /**
95 * Ensure that the global variables table exists.
96 *
97 * This method configures a database table to store MySQL global variables
98 * and other internal configuration values.
99 */
100 private function ensure_global_variables_table(): void {
101 $this->driver->execute_sqlite_query(
102 sprintf(
103 'CREATE TABLE IF NOT EXISTS %s (name TEXT PRIMARY KEY, value TEXT)',
104 $this->driver->get_connection()->quote_identifier(
105 WP_MySQL_On_SQLite::GLOBAL_VARIABLES_TABLE_NAME
106 )
107 )
108 );
109 }
110
111 /**
112 * Ensure that the database data is correctly populated.
113 *
114 * This method ensures that the "INFORMATION_SCHEMA.SCHEMATA" table contains
115 * records for both the "INFORMATION_SCHEMA" database and the user database.
116 * At the moment, only a single user database is supported.
117 *
118 * Additionally, this method ensures that the user database name is stored
119 * correctly in all the information schema tables.
120 */
121 public function ensure_database_data(): void {
122 // Get all databases from the "SCHEMATA" table.
123 $schemata_table = $this->schema_builder->get_table_name( false, 'schemata' );
124 $databases = $this->driver->execute_sqlite_query(
125 sprintf(
126 'SELECT SCHEMA_NAME FROM %s',
127 $this->driver->get_connection()->quote_identifier( $schemata_table )
128 )
129 )->fetchAll( PDO::FETCH_COLUMN ); // phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
130
131 // Ensure that the "INFORMATION_SCHEMA" database record exists.
132 if ( ! in_array( 'information_schema', $databases, true ) ) {
133 $this->driver->execute_sqlite_query(
134 sprintf(
135 'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME) VALUES (?, ?, ?)',
136 $this->driver->get_connection()->quote_identifier( $schemata_table )
137 ),
138 // The "INFORMATION_SCHEMA" database stays on "utf8mb3" even in MySQL 8 and 9.
139 array( 'information_schema', 'utf8mb3', 'utf8mb3_general_ci' )
140 );
141 }
142
143 // Get the existing user database name.
144 $existing_user_db_name = null;
145 foreach ( $databases as $database ) {
146 if ( 'information_schema' !== strtolower( $database ) ) {
147 $existing_user_db_name = $database;
148 break;
149 }
150 }
151
152 // Ensure that the user database record exists.
153 if ( null === $existing_user_db_name ) {
154 $existing_user_db_name = WP_SQLite_Information_Schema_Builder::SAVED_DATABASE_NAME;
155 $this->driver->execute_sqlite_query(
156 sprintf(
157 'INSERT INTO %s (SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME) VALUES (?, ?, ?)',
158 $this->driver->get_connection()->quote_identifier( $schemata_table )
159 ),
160 // @TODO: This should probably be version-dependent.
161 // Before MySQL 8, the default was different.
162 array( $existing_user_db_name, 'utf8mb4', 'utf8mb4_0900_ai_ci' )
163 );
164 }
165
166 // Migrate from older versions without dynamic database names.
167 $saved_database_name = WP_SQLite_Information_Schema_Builder::SAVED_DATABASE_NAME;
168 if ( $saved_database_name !== $existing_user_db_name ) {
169 // INFORMATION_SCHEMA.SCHEMATA
170 $this->driver->execute_sqlite_query(
171 sprintf(
172 "UPDATE %s SET SCHEMA_NAME = ? WHERE SCHEMA_NAME != 'information_schema'",
173 $this->driver->get_connection()->quote_identifier( $schemata_table )
174 ),
175 array( $saved_database_name )
176 );
177
178 // INFORMATION_SCHEMA.TABLES
179 $tables_table = $this->schema_builder->get_table_name( false, 'tables' );
180 $this->driver->execute_sqlite_query(
181 sprintf(
182 "UPDATE %s SET TABLE_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
183 $this->driver->get_connection()->quote_identifier( $tables_table )
184 ),
185 array( $saved_database_name )
186 );
187
188 // INFORMATION_SCHEMA.COLUMNS
189 $columns_table = $this->schema_builder->get_table_name( false, 'columns' );
190 $this->driver->execute_sqlite_query(
191 sprintf(
192 "UPDATE %s SET TABLE_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
193 $this->driver->get_connection()->quote_identifier( $columns_table )
194 ),
195 array( $saved_database_name )
196 );
197
198 // INFORMATION_SCHEMA.STATISTICS
199 $statistics_table = $this->schema_builder->get_table_name( false, 'statistics' );
200 $this->driver->execute_sqlite_query(
201 sprintf(
202 "UPDATE %s SET TABLE_SCHEMA = ?, INDEX_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
203 $this->driver->get_connection()->quote_identifier( $statistics_table )
204 ),
205 array( $saved_database_name, $saved_database_name )
206 );
207
208 // INFORMATION_SCHEMA.TABLE_CONSTRAINTS
209 $table_constraints_table = $this->schema_builder->get_table_name( false, 'table_constraints' );
210 $this->driver->execute_sqlite_query(
211 sprintf(
212 "UPDATE %s SET TABLE_SCHEMA = ?, CONSTRAINT_SCHEMA = ? WHERE TABLE_SCHEMA != 'information_schema'",
213 $this->driver->get_connection()->quote_identifier( $table_constraints_table )
214 ),
215 array( $saved_database_name, $saved_database_name )
216 );
217
218 // INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
219 $referential_constraints_table = $this->schema_builder->get_table_name( false, 'referential_constraints' );
220 $this->driver->execute_sqlite_query(
221 sprintf(
222 "UPDATE %s SET CONSTRAINT_SCHEMA = ?, UNIQUE_CONSTRAINT_SCHEMA = ? WHERE CONSTRAINT_SCHEMA != 'information_schema'",
223 $this->driver->get_connection()->quote_identifier( $referential_constraints_table )
224 ),
225 array( $saved_database_name, $saved_database_name )
226 );
227
228 // INFORMATION_SCHEMA.KEY_COLUMN_USAGE
229 $key_column_usage_table = $this->schema_builder->get_table_name( false, 'key_column_usage' );
230 $this->driver->execute_sqlite_query(
231 sprintf(
232 "UPDATE %s
233 SET
234 TABLE_SCHEMA = ?,
235 CONSTRAINT_SCHEMA = ?,
236 REFERENCED_TABLE_SCHEMA = CASE WHEN REFERENCED_TABLE_SCHEMA IS NULL THEN NULL ELSE ? END
237 WHERE TABLE_SCHEMA != 'information_schema'",
238 $this->driver->get_connection()->quote_identifier( $key_column_usage_table )
239 ),
240 array( $saved_database_name, $saved_database_name, $saved_database_name )
241 );
242
243 // INFORMATION_SCHEMA.CHECK_CONSTRAINTS
244 $check_constraints_table = $this->schema_builder->get_table_name( false, 'check_constraints' );
245 $this->driver->execute_sqlite_query(
246 sprintf(
247 "UPDATE %s SET CONSTRAINT_SCHEMA = ? WHERE CONSTRAINT_SCHEMA != 'information_schema'",
248 $this->driver->get_connection()->quote_identifier( $check_constraints_table )
249 ),
250 array( $saved_database_name )
251 );
252 }
253 }
254
255 /**
256 * Save the current SQLite driver version.
257 *
258 * This method saves the current SQLite driver version to the database.
259 */
260 private function save_current_driver_version(): void {
261 $this->driver->execute_sqlite_query(
262 sprintf(
263 'INSERT INTO %s (name, value) VALUES (?, ?) ON CONFLICT(name) DO UPDATE SET value = ?',
264 $this->driver->get_connection()->quote_identifier(
265 WP_MySQL_On_SQLite::GLOBAL_VARIABLES_TABLE_NAME
266 )
267 ),
268 array(
269 WP_MySQL_On_SQLite::DRIVER_VERSION_VARIABLE_NAME,
270 SQLITE_DRIVER_VERSION,
271 SQLITE_DRIVER_VERSION,
272 )
273 );
274 }
275 }
276