diff --git a/.github/workflows/verify-version.yml b/.github/workflows/verify-version.yml new file mode 100644 index 0000000..9bc9a61 --- /dev/null +++ b/.github/workflows/verify-version.yml @@ -0,0 +1,35 @@ +name: Verify plugin version + +on: + push: + branches: + - main + pull_request: + +jobs: + verify-version: + name: Assert the WordPress plugin header declares the same version as the SQLITE_DRIVER_VERSION constant + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Extract version from "load.php" + id: load_version + run: | + VERSION=$(grep "Version:" load.php | sed "s/.*Version: \([^ ]*\).*/\1/") + echo "load_version=$VERSION" >> $GITHUB_OUTPUT + + - name: Extract version from "version.php" + id: const_version + run: | + VERSION=$(php -r "require 'version.php'; echo SQLITE_DRIVER_VERSION;") + echo "const_version=$VERSION" >> $GITHUB_OUTPUT + + - name: Compare versions + run: | + if [ "${{ steps.load_version.outputs.load_version }}" != "${{ steps.const_version.outputs.const_version }}" ]; then + echo "Version mismatch detected!" + echo " load.php version: ${{ steps.load_version.outputs.load_version }}" + echo " version.php constant: ${{ steps.const_version.outputs.const_version }}" + exit 1 + fi diff --git a/load.php b/load.php index 4e082ee..c9c95e1 100644 --- a/load.php +++ b/load.php @@ -12,6 +12,12 @@ * @package wp-sqlite-integration */ +/** + * Load the "SQLITE_DRIVER_VERSION" constant. + * This constant needs to be updated on plugin release! + */ +require_once __DIR__ . '/version.php'; + define( 'SQLITE_MAIN_FILE', __FILE__ ); require_once __DIR__ . '/php-polyfills.php'; diff --git a/tests/WP_SQLite_Driver_Metadata_Tests.php b/tests/WP_SQLite_Driver_Metadata_Tests.php index 33625b7..23c287f 100644 --- a/tests/WP_SQLite_Driver_Metadata_Tests.php +++ b/tests/WP_SQLite_Driver_Metadata_Tests.php @@ -1,44 +1,20 @@ suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; // Before each test, we create a new database public function setUp(): void { $this->sqlite = new PDO( 'sqlite::memory:' ); $this->engine = new WP_SQLite_Driver( - array( - 'connection' => $this->sqlite, - 'database' => 'wp', - ) + new WP_SQLite_Connection( array( 'pdo' => $this->sqlite ) ), + 'wp' ); } diff --git a/tests/WP_SQLite_Driver_Query_Tests.php b/tests/WP_SQLite_Driver_Query_Tests.php index 51b6b13..636813f 100644 --- a/tests/WP_SQLite_Driver_Query_Tests.php +++ b/tests/WP_SQLite_Driver_Query_Tests.php @@ -1,38 +1,16 @@ suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; /** * Before each test, we create a new volatile database and WordPress tables. @@ -47,10 +25,8 @@ public function setUp(): void { $this->sqlite = new PDO( 'sqlite::memory:' ); $this->engine = new WP_SQLite_Driver( - array( - 'connection' => $this->sqlite, - 'database' => 'wp', - ) + new WP_SQLite_Connection( array( 'pdo' => $this->sqlite ) ), + 'wp' ); $translator = $this->engine; @@ -462,7 +438,7 @@ public function testRecoverSerialized() { ); $option_name = 'serialized_option'; $option_value = serialize( $obj ); - $option_value_escaped = $this->engine->get_pdo()->quote( $option_value ); + $option_value_escaped = $this->engine->get_connection()->quote( $option_value ); /* Note well: this is heredoc not nowdoc */ $insert = <<engine->get_pdo()->quote( $option_value ); + $option_value_escaped = $this->engine->get_connection()->quote( $option_value ); /* Note well: this is heredoc not nowdoc */ $insert = <<suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; // Before each test, we create a new database public function setUp(): void { $this->sqlite = new PDO( 'sqlite::memory:' ); $this->engine = new WP_SQLite_Driver( - array( - 'connection' => $this->sqlite, - 'database' => 'wp', - ) + new WP_SQLite_Connection( array( 'pdo' => $this->sqlite ) ), + 'wp' ); $this->engine->query( "CREATE TABLE _options ( @@ -1234,14 +1209,14 @@ public function testColumnWithOnUpdate() { 'name' => '_wp_sqlite__tmp_table_created_at_on_update', 'tbl_name' => '_tmp_table', 'rootpage' => '0', - 'sql' => "CREATE TRIGGER \"_wp_sqlite__tmp_table_created_at_on_update\"\n\t\t\tAFTER UPDATE ON \"_tmp_table\"\n\t\t\tFOR EACH ROW\n\t\t\tBEGIN\n\t\t\t UPDATE \"_tmp_table\" SET \"created_at\" = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;\n\t\t\tEND", + 'sql' => "CREATE TRIGGER `_wp_sqlite__tmp_table_created_at_on_update`\n\t\t\t\tAFTER UPDATE ON `_tmp_table`\n\t\t\t\tFOR EACH ROW\n\t\t\t\tBEGIN\n\t\t\t\t UPDATE `_tmp_table` SET `created_at` = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;\n\t\t\t\tEND", ), (object) array( 'type' => 'trigger', 'name' => '_wp_sqlite__tmp_table_updated_at_on_update', 'tbl_name' => '_tmp_table', 'rootpage' => '0', - 'sql' => "CREATE TRIGGER \"_wp_sqlite__tmp_table_updated_at_on_update\"\n\t\t\tAFTER UPDATE ON \"_tmp_table\"\n\t\t\tFOR EACH ROW\n\t\t\tBEGIN\n\t\t\t UPDATE \"_tmp_table\" SET \"updated_at\" = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;\n\t\t\tEND", + 'sql' => "CREATE TRIGGER `_wp_sqlite__tmp_table_updated_at_on_update`\n\t\t\t\tAFTER UPDATE ON `_tmp_table`\n\t\t\t\tFOR EACH ROW\n\t\t\t\tBEGIN\n\t\t\t\t UPDATE `_tmp_table` SET `updated_at` = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;\n\t\t\t\tEND", ), ), $results @@ -4488,4 +4463,10 @@ public function testSessionSqlModes(): void { $result = $this->assertQuery( 'SELECT @@session.SQL_mode' ); $this->assertSame( 'ONLY_FULL_GROUP_BY', $result[0]->{'@@session.SQL_mode'} ); } + + public function testMultiQueryNotSupported(): void { + $this->expectException( WP_SQLite_Driver_Exception::class ); + $this->expectExceptionMessage( 'Multi-query is not supported.' ); + $this->assertQuery( 'SELECT 1; SELECT 2' ); + } } diff --git a/tests/WP_SQLite_Driver_Translation_Tests.php b/tests/WP_SQLite_Driver_Translation_Tests.php index f7340e8..1a80dc1 100644 --- a/tests/WP_SQLite_Driver_Translation_Tests.php +++ b/tests/WP_SQLite_Driver_Translation_Tests.php @@ -1,9 +1,5 @@ driver = new WP_SQLite_Driver( - array( - 'path' => ':memory:', - 'database' => 'wp', - ) + new WP_SQLite_Connection( array( 'path' => ':memory:' ) ), + 'wp' ); } @@ -212,13 +206,13 @@ public function testCreateTable(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -231,17 +225,17 @@ public function testCreateTableWithMultipleColumns(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'name', 2, null, 'YES', 'text', 65535, 65535, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'text', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'score', 3, '0.0', 'YES', 'float', null, null, 12, null, null, null, null, 'float', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -254,15 +248,15 @@ public function testCreateTableWithBasicConstraints(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'NO', 'int', null, null, 10, 0, null, null, null, 'int', 'PRI', 'auto_increment', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'PRIMARY', 1, 'id', 'A', 0, null, null, '', 'BTREE', '', '', 'YES', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -276,13 +270,13 @@ public function testCreateTableWithEngine(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'MyISAM', 'Fixed', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -296,13 +290,13 @@ public function testCreateTableWithCollate(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_czech_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -324,15 +318,15 @@ public function testCreateTableWithPrimaryKey(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'NO', 'int', null, null, 10, 0, null, null, null, 'int', 'PRI', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'PRIMARY', 1, 'id', 'A', 0, null, null, '', 'BTREE', '', '', 'YES', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -346,15 +340,15 @@ public function testCreateTableWithPrimaryKeyAndAutoincrement(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't1', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't1', 'id', 1, null, 'NO', 'int', null, null, 10, 0, null, null, null, 'int', 'PRI', 'auto_increment', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't1', 0, 'wp', 'PRIMARY', 1, 'id', 'A', 0, null, null, '', 'BTREE', '', '', 'YES', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't1'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't1'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't1'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't1'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't1'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't1'", ) ); @@ -366,15 +360,15 @@ public function testCreateTableWithPrimaryKeyAndAutoincrement(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't2', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't2', 'id', 1, null, 'NO', 'int', null, null, 10, 0, null, null, null, 'int', 'PRI', 'auto_increment', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't2', 0, 'wp', 'PRIMARY', 1, 'id', 'A', 0, null, null, '', 'BTREE', '', '', 'YES', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't2'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't2'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't2'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't2'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't2'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't2'", ) ); @@ -386,17 +380,17 @@ public function testCreateTableWithPrimaryKeyAndAutoincrement(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't3', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't3', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', 'auto_increment', 'select,insert,update,references', '', '', null)", - "SELECT column_name, data_type, is_nullable, character_maximum_length FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't3' AND column_name IN ('id')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + "SELECT column_name, data_type, is_nullable, character_maximum_length FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't3' AND column_name IN ('id')", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't3', 0, 'wp', 'PRIMARY', 1, 'id', 'A', 0, null, null, '', 'BTREE', '', '', 'YES', null)", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't3' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't3' AND s.column_name = c.column_name", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't3'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't3'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't3'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't3' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't3' AND s.column_name = c.column_name", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't3'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't3'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't3'", ) ); } @@ -413,19 +407,19 @@ public function testCreateTableWithInlineUniqueIndexes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', 'UNI', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'id', 1, 'id', 'A', 0, null, null, 'YES', 'BTREE', '', '', 'YES', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'name', 2, null, 'YES', 'text', 65535, 65535, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'text', 'UNI', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'name', 1, 'name', 'A', 0, null, null, 'YES', 'BTREE', '', '', 'YES', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -442,23 +436,23 @@ public function testCreateTableWithStandaloneUniqueIndexes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'name', 2, null, 'YES', 'varchar', 100, 400, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'varchar(100)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT column_name, data_type, is_nullable, character_maximum_length FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name IN ('id')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + "SELECT column_name, data_type, is_nullable, character_maximum_length FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't' AND column_name IN ('id')", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'id', 1, 'id', 'A', 0, null, null, 'YES', 'BTREE', '', '', 'YES', null)", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT column_name, data_type, is_nullable, character_maximum_length FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name IN ('name')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_statistics (table_schema, table_name, non_unique, index_schema, index_name, seq_in_index, column_name, collation, cardinality, sub_part, packed, nullable, index_type, comment, index_comment, is_visible, expression)' + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT column_name, data_type, is_nullable, character_maximum_length FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't' AND column_name IN ('name')", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_statistics` (`table_schema`, `table_name`, `non_unique`, `index_schema`, `index_name`, `seq_in_index`, `column_name`, `collation`, `cardinality`, `sub_part`, `packed`, `nullable`, `index_type`, `comment`, `index_comment`, `is_visible`, `expression`)' . " VALUES ('wp', 't', 0, 'wp', 'name', 1, 'name', 'A', 0, null, null, 'YES', 'BTREE', '', '', 'YES', null)", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -520,13 +514,13 @@ public function testAlterTableAddColumn(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 2, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -549,13 +543,13 @@ public function testAlterTableAddColumnWithNotNull(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 2, null, 'NO', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -578,13 +572,13 @@ public function testAlterTableAddColumnWithDefault(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 2, '0', 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -607,13 +601,13 @@ public function testAlterTableAddColumnWithNotNullAndDefault(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 2, '0', 'NO', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -636,19 +630,19 @@ public function testAlterTableAddMultipleColumns(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 2, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b', 3, null, 'YES', 'text', 65535, 65535, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'text', '', '', 'select,insert,update,references', '', '', null)", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c', 4, null, 'YES', 'tinyint', null, null, 3, 0, null, null, null, 'tinyint(1)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -671,13 +665,13 @@ public function testAlterTableDropColumn(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "DELETE FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "DELETE FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_columns` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -699,16 +693,16 @@ public function testAlterTableDropMultipleColumns(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "DELETE FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "DELETE FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "DELETE FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'b'", - "DELETE FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'b'", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_columns` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "DELETE FROM `_wp_sqlite_mysql_information_schema_columns` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'b'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'b'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -731,16 +725,16 @@ public function testAlterTableAddAndDropColumns(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b', 2, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "DELETE FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "DELETE FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_columns` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -763,16 +757,16 @@ public function testAlterTableDropAndAddSingleColumn(): void { $this->assertExecutedInformationSchemaQueries( array( - "SELECT COLUMN_NAME FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "DELETE FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "DELETE FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' AND column_name = 'a'", - "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE _wp_sqlite_mysql_information_schema_columns AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", - "SELECT MAX(ordinal_position) FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + "SELECT COLUMN_NAME FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_columns` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "DELETE FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE `table_schema` = 'wp' AND `table_name` = 't' AND `column_name` = 'a'", + "WITH s AS ( SELECT column_name, CASE WHEN MAX(index_name = 'PRIMARY') THEN 'PRI' WHEN MAX(non_unique = 0 AND seq_in_index = 1) THEN 'UNI' WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't' GROUP BY column_name ) UPDATE `_wp_sqlite_mysql_information_schema_columns` AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) FROM s WHERE c.table_schema = 'wp' AND c.table_name = 't' AND s.column_name = c.column_name", + "SELECT MAX(ordinal_position) FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'a', 1, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -785,15 +779,15 @@ public function testBitDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i1', 1, null, 'YES', 'bit', null, null, 1, null, null, null, null, 'bit(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i2', 2, null, 'YES', 'bit', null, null, 10, null, null, null, null, 'bit(10)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -806,15 +800,15 @@ public function testBooleanDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i1', 1, null, 'YES', 'tinyint', null, null, 3, 0, null, null, null, 'tinyint(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i2', 2, null, 'YES', 'tinyint', null, null, 3, 0, null, null, null, 'tinyint(1)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -827,23 +821,23 @@ public function testIntegerDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i1', 1, null, 'YES', 'tinyint', null, null, 3, 0, null, null, null, 'tinyint', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i2', 2, null, 'YES', 'smallint', null, null, 5, 0, null, null, null, 'smallint', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i3', 3, null, 'YES', 'mediumint', null, null, 7, 0, null, null, null, 'mediumint', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i4', 4, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i5', 5, null, 'YES', 'int', null, null, 10, 0, null, null, null, 'int', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'i6', 6, null, 'YES', 'bigint', null, null, 19, 0, null, null, null, 'bigint', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -856,19 +850,19 @@ public function testFloatDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f1', 1, null, 'YES', 'float', null, null, 12, null, null, null, null, 'float', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f2', 2, null, 'YES', 'double', null, null, 22, null, null, null, null, 'double', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f3', 3, null, 'YES', 'double', null, null, 22, null, null, null, null, 'double', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f4', 4, null, 'YES', 'double', null, null, 22, null, null, null, null, 'double', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -881,19 +875,19 @@ public function testDecimalTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f1', 1, null, 'YES', 'decimal', null, null, 10, 0, null, null, null, 'decimal(10,0)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f2', 2, null, 'YES', 'decimal', null, null, 10, 0, null, null, null, 'decimal(10,0)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f3', 3, null, 'YES', 'decimal', null, null, 10, 0, null, null, null, 'decimal(10,0)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'f4', 4, null, 'YES', 'decimal', null, null, 10, 0, null, null, null, 'decimal(10,0)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -906,15 +900,15 @@ public function testCharDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c1', 1, null, 'YES', 'char', 1, 4, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'char(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c2', 2, null, 'YES', 'char', 10, 40, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'char(10)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -927,17 +921,17 @@ public function testVarcharDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c1', 1, null, 'YES', 'varchar', 255, 1020, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c2', 2, null, 'YES', 'varchar', 255, 1020, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c3', 3, null, 'YES', 'varchar', 255, 1020, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -950,19 +944,19 @@ public function testNationalCharDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c1', 1, null, 'YES', 'char', 1, 3, null, null, null, 'utf8', 'utf8_general_ci', 'char(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c2', 2, null, 'YES', 'char', 1, 3, null, null, null, 'utf8', 'utf8_general_ci', 'char(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c3', 3, null, 'YES', 'char', 10, 30, null, null, null, 'utf8', 'utf8_general_ci', 'char(10)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c4', 4, null, 'YES', 'char', 10, 30, null, null, null, 'utf8', 'utf8_general_ci', 'char(10)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -975,17 +969,17 @@ public function testNcharVarcharDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c1', 1, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c2', 2, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c3', 3, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -998,17 +992,17 @@ public function testNationalVarcharDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c1', 1, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c2', 2, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'c3', 3, null, 'YES', 'varchar', 255, 765, null, null, null, 'utf8', 'utf8_general_ci', 'varchar(255)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1021,19 +1015,19 @@ public function testTextDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 't1', 1, null, 'YES', 'tinytext', 255, 255, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'tinytext', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 't2', 2, null, 'YES', 'text', 65535, 65535, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'text', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 't3', 3, null, 'YES', 'mediumtext', 16777215, 16777215, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'mediumtext', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 't4', 4, null, 'YES', 'longtext', 4294967295, 4294967295, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'longtext', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1046,13 +1040,13 @@ public function testEnumDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'e', 1, null, 'YES', 'enum', 1, 4, null, null, null, 'utf8mb4', 'utf8mb4_general_ci', 'enum(''a'',''b'',''c'')', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1065,21 +1059,21 @@ public function testDateAndTimeDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'd', 1, null, 'YES', 'date', null, null, null, null, null, null, null, 'date', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 't', 2, null, 'YES', 'time', null, null, null, null, 0, null, null, 'time', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'dt', 3, null, 'YES', 'datetime', null, null, null, null, 0, null, null, 'datetime', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'ts', 4, null, 'YES', 'timestamp', null, null, null, null, 0, null, null, 'timestamp', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'y', 5, null, 'YES', 'year', null, null, null, null, null, null, null, 'year', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1092,15 +1086,15 @@ public function testBinaryDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b', 1, null, 'YES', 'binary', 1, 1, null, null, null, null, null, 'binary(1)', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'v', 2, null, 'YES', 'varbinary', 255, 255, null, null, null, null, null, 'varbinary(255)', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1113,19 +1107,19 @@ public function testBlobDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b1', 1, null, 'YES', 'tinyblob', 255, 255, null, null, null, null, null, 'tinyblob', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b2', 2, null, 'YES', 'blob', 65535, 65535, null, null, null, null, null, 'blob', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b3', 3, null, 'YES', 'mediumblob', 16777215, 16777215, null, null, null, null, null, 'mediumblob', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'b4', 4, null, 'YES', 'longblob', 4294967295, 4294967295, null, null, null, null, null, 'longblob', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1138,19 +1132,19 @@ public function testBasicSpatialDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g1', 1, null, 'YES', 'geometry', null, null, null, null, null, null, null, 'geometry', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g2', 2, null, 'YES', 'point', null, null, null, null, null, null, null, 'point', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g3', 3, null, 'YES', 'linestring', null, null, null, null, null, null, null, 'linestring', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g4', 4, null, 'YES', 'polygon', null, null, null, null, null, null, null, 'polygon', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1163,17 +1157,17 @@ public function testMultiObjectSpatialDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g1', 1, null, 'YES', 'multipoint', null, null, null, null, null, null, null, 'multipoint', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g2', 2, null, 'YES', 'multilinestring', null, null, null, null, null, null, null, 'multilinestring', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g3', 3, null, 'YES', 'multipolygon', null, null, null, null, null, null, null, 'multipolygon', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1186,15 +1180,15 @@ public function testGeometryCollectionDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g1', 1, null, 'YES', 'geomcollection', null, null, null, null, null, null, null, 'geomcollection', '', '', 'select,insert,update,references', '', '', null)", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'g2', 2, null, 'YES', 'geomcollection', null, null, null, null, null, null, null, 'geomcollection', '', '', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1207,13 +1201,13 @@ public function testSerialDataTypes(): void { $this->assertExecutedInformationSchemaQueries( array( - 'INSERT INTO _wp_sqlite_mysql_information_schema_tables (table_schema, table_name, table_type, engine, row_format, table_collation)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_tables` (`table_schema`, `table_name`, `table_type`, `engine`, `row_format`, `table_collation`)' . " VALUES ('wp', 't', 'BASE TABLE', 'InnoDB', 'Dynamic', 'utf8mb4_general_ci')", - 'INSERT INTO _wp_sqlite_mysql_information_schema_columns (table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_scale, datetime_precision, character_set_name, collation_name, column_type, column_key, extra, privileges, column_comment, generation_expression, srs_id)' + 'INSERT INTO `_wp_sqlite_mysql_information_schema_columns` (`table_schema`, `table_name`, `column_name`, `ordinal_position`, `column_default`, `is_nullable`, `data_type`, `character_maximum_length`, `character_octet_length`, `numeric_precision`, `numeric_scale`, `datetime_precision`, `character_set_name`, `collation_name`, `column_type`, `column_key`, `extra`, `privileges`, `column_comment`, `generation_expression`, `srs_id`)' . " VALUES ('wp', 't', 'id', 1, null, 'NO', 'bigint', null, null, 20, 0, null, null, null, 'bigint unsigned', 'PRI', 'auto_increment', 'select,insert,update,references', '', '', null)", - "SELECT * FROM _wp_sqlite_mysql_information_schema_tables WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_columns WHERE table_schema = 'wp' AND table_name = 't'", - "SELECT * FROM _wp_sqlite_mysql_information_schema_statistics WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_tables` WHERE table_type = 'BASE TABLE' AND table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_columns` WHERE table_schema = 'wp' AND table_name = 't'", + "SELECT * FROM `_wp_sqlite_mysql_information_schema_statistics` WHERE table_schema = 'wp' AND table_name = 't'", ) ); } @@ -1393,7 +1387,7 @@ function ( $param ) { return 'null'; } if ( is_string( $param ) ) { - return $this->driver->get_pdo()->quote( $param ); + return $this->driver->get_connection()->quote( $param ); } return $param; }, diff --git a/tests/WP_SQLite_Information_Schema_Reconstructor_Tests.php b/tests/WP_SQLite_Information_Schema_Reconstructor_Tests.php new file mode 100644 index 0000000..35d3da4 --- /dev/null +++ b/tests/WP_SQLite_Information_Schema_Reconstructor_Tests.php @@ -0,0 +1,273 @@ +sqlite = new PDO( 'sqlite::memory:' ); + $this->engine = new WP_SQLite_Driver( + new WP_SQLite_Connection( array( 'pdo' => $this->sqlite ) ), + 'wp' + ); + + $builder = new WP_SQLite_Information_Schema_Builder( + 'wp', + WP_SQLite_Driver::RESERVED_PREFIX, + $this->engine->get_connection() + ); + + $this->reconstructor = new WP_SQLite_Information_Schema_Reconstructor( + $this->engine, + $builder + ); + } + + public function testReconstructTable(): void { + $this->engine->get_connection()->query( + ' + CREATE TABLE t ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE, + name TEXT NOT NULL, + role TEXT, + score REAL, + priority INTEGER DEFAULT 0, + data BLOB, + UNIQUE (name) + ) + ' + ); + $this->engine->get_connection()->query( 'CREATE INDEX idx_score ON t (score)' ); + $this->engine->get_connection()->query( 'CREATE INDEX idx_role_score ON t (role, priority)' ); + $result = $this->assertQuery( 'SELECT * FROM information_schema.tables WHERE table_name = "t"' ); + $this->assertEquals( 0, count( $result ) ); + + $this->reconstructor->ensure_correct_information_schema(); + $result = $this->assertQuery( 'SELECT * FROM information_schema.tables WHERE table_name = "t"' ); + $this->assertEquals( 1, count( $result ) ); + + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertSame( + implode( + "\n", + array( + 'CREATE TABLE `t` (', + ' `id` int NOT NULL AUTO_INCREMENT,', + ' `email` text NOT NULL,', + ' `name` text NOT NULL,', + ' `role` text DEFAULT NULL,', + ' `score` float DEFAULT NULL,', + " `priority` int DEFAULT '0',", + ' `data` blob DEFAULT NULL,', + ' PRIMARY KEY (`id`),', + ' KEY `idx_role_score` (`role`(100), `priority`),', + ' KEY `idx_score` (`score`),', + ' UNIQUE KEY `name` (`name`(100)),', + ' UNIQUE KEY `email` (`email`(100))', + ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci', + ) + ), + $result[0]->{'Create Table'} + ); + } + + public function testReconstructWpTable(): void { + // Create a WP table with any columns. + $this->engine->get_connection()->query( 'CREATE TABLE wp_posts ( id INTEGER )' ); + + // Reconstruct the information schema. + $this->reconstructor->ensure_correct_information_schema(); + $result = $this->assertQuery( 'SELECT * FROM information_schema.tables WHERE table_name = "wp_posts"' ); + $this->assertEquals( 1, count( $result ) ); + + // The reconstructed schema should correspond to the original WP table definition. + $result = $this->assertQuery( 'SHOW CREATE TABLE wp_posts' ); + $this->assertSame( + implode( + "\n", + array( + 'CREATE TABLE `wp_posts` (', + ' `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,', + " `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',", + " `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',", + " `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',", + ' `post_content` longtext NOT NULL,', + ' `post_title` text NOT NULL,', + ' `post_excerpt` text NOT NULL,', + " `post_status` varchar(20) NOT NULL DEFAULT 'publish',", + " `comment_status` varchar(20) NOT NULL DEFAULT 'open',", + " `ping_status` varchar(20) NOT NULL DEFAULT 'open',", + " `post_password` varchar(255) NOT NULL DEFAULT '',", + " `post_name` varchar(200) NOT NULL DEFAULT '',", + ' `to_ping` text NOT NULL,', + ' `pinged` text NOT NULL,', + " `post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',", + " `post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',", + ' `post_content_filtered` longtext NOT NULL,', + " `post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',", + " `guid` varchar(255) NOT NULL DEFAULT '',", + " `menu_order` int(11) NOT NULL DEFAULT '0',", + " `post_type` varchar(20) NOT NULL DEFAULT 'post',", + " `post_mime_type` varchar(100) NOT NULL DEFAULT '',", + " `comment_count` bigint(20) NOT NULL DEFAULT '0',", + ' PRIMARY KEY (`ID`),', + ' KEY `post_name` (`post_name`(191)),', + ' KEY `type_status_date` (`post_type`, `post_status`, `post_date`, `ID`),', + ' KEY `post_parent` (`post_parent`),', + ' KEY `post_author` (`post_author`)', + ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci', + ) + ), + $result[0]->{'Create Table'} + ); + } + + public function testReconstructTableFromMysqlDataTypesCache(): void { + $connection = $this->engine->get_connection(); + + $connection->query( self::CREATE_DATA_TYPES_CACHE_TABLE_SQL ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 'id', 'int unsigned')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 'name', 'varchar(255)')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 'description', 'text')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 'shape', 'geomcollection')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 't__idx_name', 'KEY')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 't__idx_description', 'FULLTEXT')" ); + $connection->query( "INSERT INTO _mysql_data_types_cache (`table`, column_or_index, mysql_type) VALUES ('t', 't__idx_shape', 'SPATIAL')" ); + + $connection->query( + ' + CREATE TABLE t ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + description TEXT, + shape TEXT NOT NULL + ) + ' + ); + $connection->query( 'CREATE INDEX t__idx_name ON t (name)' ); + $connection->query( 'CREATE INDEX t__idx_description ON t (description)' ); + $connection->query( 'CREATE INDEX t__idx_shape ON t (shape)' ); + + $this->reconstructor->ensure_correct_information_schema(); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertSame( + implode( + "\n", + array( + 'CREATE TABLE `t` (', + ' `id` int unsigned NOT NULL AUTO_INCREMENT,', + ' `name` varchar(255) DEFAULT NULL,', + ' `description` text DEFAULT NULL,', + ' `shape` geomcollection NOT NULL,', + ' PRIMARY KEY (`id`),', + ' SPATIAL KEY `idx_shape` (`shape`(32)),', + ' FULLTEXT KEY `idx_description` (`description`(100)),', + ' KEY `idx_name` (`name`(100))', + ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci', + ) + ), + $result[0]->{'Create Table'} + ); + } + + public function testDefaultValues(): void { + $this->engine->get_connection()->query( + " + CREATE TABLE t ( + col1 text DEFAULT abc, + col2 text DEFAULT 'abc', + col3 text DEFAULT \"abc\", + col4 text DEFAULT NULL, + col5 int DEFAULT TRUE, + col6 int DEFAULT FALSE, + col7 int DEFAULT 123, + col8 real DEFAULT 1.23, + col9 real DEFAULT -1.23, + col10 real DEFAULT 1e3, + col11 real DEFAULT 1.2e-3, + col12 int DEFAULT 0x1a2f, + col13 int DEFAULT 0X1A2f, + col14 blob DEFAULT x'4142432E', + col15 blob DEFAULT x'4142432E', + col16 text DEFAULT CURRENT_TIMESTAMP, + col17 text DEFAULT CURRENT_DATE, + col18 text DEFAULT CURRENT_TIME + ) + " + ); + + $this->reconstructor->ensure_correct_information_schema(); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertSame( + implode( + "\n", + array( + 'CREATE TABLE `t` (', + " `col1` varchar(65535) DEFAULT 'abc',", + " `col2` varchar(65535) DEFAULT 'abc',", + " `col3` varchar(65535) DEFAULT 'abc',", + ' `col4` text DEFAULT NULL,', + " `col5` int DEFAULT '1',", + " `col6` int DEFAULT '0',", + " `col7` int DEFAULT '123',", + " `col8` float DEFAULT '1.23',", + " `col9` float DEFAULT '-1.23',", + " `col10` float DEFAULT '1e3',", + " `col11` float DEFAULT '1.2e-3',", + " `col12` int DEFAULT '6703',", + " `col13` int DEFAULT '6703',", + " `col14` varbinary(65535) DEFAULT 'ABC.',", + " `col15` varbinary(65535) DEFAULT 'ABC.',", + ' `col16` datetime DEFAULT CURRENT_TIMESTAMP,', + ' `col17` text DEFAULT NULL,', + ' `col18` text DEFAULT NULL', + ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci', + ) + ), + $result[0]->{'Create Table'} + ); + } + + private function assertQuery( $sql ) { + $retval = $this->engine->query( $sql ); + $this->assertNotFalse( $retval ); + return $retval; + } +} diff --git a/tests/WP_SQLite_Metadata_Tests.php b/tests/WP_SQLite_Metadata_Tests.php index cc66b35..399edb5 100644 --- a/tests/WP_SQLite_Metadata_Tests.php +++ b/tests/WP_SQLite_Metadata_Tests.php @@ -3,29 +3,11 @@ use PHPUnit\Framework\TestCase; class WP_SQLite_Metadata_Tests extends TestCase { - + /** @var WP_SQLite_Translator */ private $engine; - private $sqlite; - public static function setUpBeforeClass(): void { - // if ( ! defined( 'PDO_DEBUG' )) { - // define( 'PDO_DEBUG', true ); - // } - if ( ! defined( 'FQDB' ) ) { - define( 'FQDB', ':memory:' ); - define( 'FQDBDIR', __DIR__ . '/../testdb' ); - } - error_reporting( E_ALL & ~E_DEPRECATED ); - if ( ! isset( $GLOBALS['table_prefix'] ) ) { - $GLOBALS['table_prefix'] = 'wptests_'; - } - if ( ! isset( $GLOBALS['wpdb'] ) ) { - $GLOBALS['wpdb'] = new stdClass(); - $GLOBALS['wpdb']->suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; // Before each test, we create a new database public function setUp(): void { diff --git a/tests/WP_SQLite_Query_Tests.php b/tests/WP_SQLite_Query_Tests.php index ff6e0aa..648cd4e 100644 --- a/tests/WP_SQLite_Query_Tests.php +++ b/tests/WP_SQLite_Query_Tests.php @@ -6,29 +6,11 @@ * Unit tests using the WordPress table definitions. */ class WP_SQLite_Query_Tests extends TestCase { - + /** @var WP_SQLite_Translator */ private $engine; - private $sqlite; - public static function setUpBeforeClass(): void { - // if ( ! defined( 'PDO_DEBUG' )) { - // define( 'PDO_DEBUG', true ); - // } - if ( ! defined( 'FQDB' ) ) { - define( 'FQDB', ':memory:' ); - define( 'FQDBDIR', __DIR__ . '/../testdb' ); - } - error_reporting( E_ALL & ~E_DEPRECATED ); - if ( ! isset( $GLOBALS['table_prefix'] ) ) { - $GLOBALS['table_prefix'] = 'wptests_'; - } - if ( ! isset( $GLOBALS['wpdb'] ) ) { - $GLOBALS['wpdb'] = new stdClass(); - $GLOBALS['wpdb']->suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; /** * Before each test, we create a new volatile database and WordPress tables. diff --git a/tests/WP_SQLite_Translator_Tests.php b/tests/WP_SQLite_Translator_Tests.php index d68a6b1..a892fe7 100644 --- a/tests/WP_SQLite_Translator_Tests.php +++ b/tests/WP_SQLite_Translator_Tests.php @@ -3,29 +3,11 @@ use PHPUnit\Framework\TestCase; class WP_SQLite_Translator_Tests extends TestCase { - + /** @var WP_SQLite_Translator */ private $engine; - private $sqlite; - public static function setUpBeforeClass(): void { - // if ( ! defined( 'PDO_DEBUG' )) { - // define( 'PDO_DEBUG', true ); - // } - if ( ! defined( 'FQDB' ) ) { - define( 'FQDB', ':memory:' ); - define( 'FQDBDIR', __DIR__ . '/../testdb' ); - } - error_reporting( E_ALL & ~E_DEPRECATED ); - if ( ! isset( $GLOBALS['table_prefix'] ) ) { - $GLOBALS['table_prefix'] = 'wptests_'; - } - if ( ! isset( $GLOBALS['wpdb'] ) ) { - $GLOBALS['wpdb'] = new stdClass(); - $GLOBALS['wpdb']->suppress_errors = false; - $GLOBALS['wpdb']->show_errors = true; - } - return; - } + /** @var PDO */ + private $sqlite; // Before each test, we create a new database public function setUp(): void { diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4ba0fc7..d46d01c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,6 +1,7 @@ ':memory:', - 'database' => 'wp', - ) + new WP_SQLite_Connection( array( 'path' => ':memory:' ) ), + 'wp' ); $query = "SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.t1_id WHERE t1.name = 'abc'"; diff --git a/version.php b/version.php new file mode 100644 index 0000000..b933657 --- /dev/null +++ b/version.php @@ -0,0 +1,8 @@ +token_type ) { return null; } - return new WP_MySQL_Token( $this->token_type, $this->get_current_token_bytes() ); + return new WP_MySQL_Token( + $this->token_type, + $this->token_starts_at, + $this->bytes_already_read - $this->token_starts_at, + $this->sql + ); } /** diff --git a/wp-includes/mysql/class-wp-mysql-parser.php b/wp-includes/mysql/class-wp-mysql-parser.php index c96978e..f291064 100644 --- a/wp-includes/mysql/class-wp-mysql-parser.php +++ b/wp-includes/mysql/class-wp-mysql-parser.php @@ -1,4 +1,52 @@ next_query() ) { + * $ast = $parser->get_query_ast(); + * if ( ! $ast ) { + * // The parsing failed. + * } + * // The query was successfully parsed. + * } + * + * @return bool Whether a query was successfully parsed. + */ + public function next_query(): bool { + if ( $this->position >= count( $this->tokens ) ) { + return false; + } + $this->current_ast = $this->parse(); + return true; + } + + /** + * Get the current query AST. + * + * When no query has been parsed yet, the parsing failed, or the end of the + * input was reached, this method returns null. + * + * @see WP_MySQL_Parser::next_query() for usage example. + * + * @return WP_Parser_Node|null The current query AST, or null if no query was parsed. + */ + public function get_query_ast(): ?WP_Parser_Node { + return $this->current_ast; + } } diff --git a/wp-includes/mysql/class-wp-mysql-token.php b/wp-includes/mysql/class-wp-mysql-token.php index c812288..b0e6a1b 100644 --- a/wp-includes/mysql/class-wp-mysql-token.php +++ b/wp-includes/mysql/class-wp-mysql-token.php @@ -34,6 +34,6 @@ public function get_name(): string { * @return string */ public function __toString(): string { - return $this->value . '<' . $this->id . ',' . $this->get_name() . '>'; + return $this->get_value() . '<' . $this->id . ',' . $this->get_name() . '>'; } } diff --git a/wp-includes/parser/class-wp-parser-node.php b/wp-includes/parser/class-wp-parser-node.php index 9d66233..aa207bf 100644 --- a/wp-includes/parser/class-wp-parser-node.php +++ b/wp-includes/parser/class-wp-parser-node.php @@ -263,6 +263,27 @@ public function get_descendant_tokens( ?int $token_id = null ): array { return $all_descendants; } + /** + * Get the byte offset in the input SQL string where this node begins. + * + * @return int + */ + public function get_start(): int { + return $this->get_first_descendant_token()->start; + } + + /** + * Get the byte length of this node in the input SQL string. + * + * @return int + */ + public function get_length(): int { + $tokens = $this->get_descendant_tokens(); + $last_token = end( $tokens ); + $start = $this->get_start(); + return $last_token->start + $last_token->length - $start; + } + /* * @TODO: Let's implement a more powerful AST-querying API. * See: https://github.com/WordPress/sqlite-database-integration/pull/164#discussion_r1855230501 diff --git a/wp-includes/parser/class-wp-parser-token.php b/wp-includes/parser/class-wp-parser-token.php index 1148995..7e411be 100644 --- a/wp-includes/parser/class-wp-parser-token.php +++ b/wp-includes/parser/class-wp-parser-token.php @@ -17,20 +17,52 @@ class WP_Parser_Token { public $id; /** - * Token value in its original raw form. + * Byte offset in the input where the token begins. + * + * @var int + */ + public $start; + + /** + * Byte length of the token in the input. + * + * @var int + */ + public $length; + + /** + * Input bytes from which the token was parsed. * * @var string */ - public $value; + private $input; /** * Constructor. * - * @param int $id Token type. - * @param string $value Token value. + * @param int $id Token type. + * @param int $start Byte offset in the input where the token begins. + * @param int $length Byte length of the token in the input. + * @param string $input Input bytes from which the token was parsed. + */ + public function __construct( + int $id, + int $start, + int $length, + string $input + ) { + $this->id = $id; + $this->start = $start; + $this->length = $length; + $this->input = $input; + } + + /** + * Get the token value as raw bytes from the input. + * + * @return string The token value. */ - public function __construct( int $id, string $value ) { - $this->id = $id; - $this->value = $value; + public function get_value(): string { + return substr( $this->input, $this->start, $this->length ); } } diff --git a/wp-includes/parser/class-wp-parser.php b/wp-includes/parser/class-wp-parser.php index f266cc7..4436892 100644 --- a/wp-includes/parser/class-wp-parser.php +++ b/wp-includes/parser/class-wp-parser.php @@ -9,9 +9,9 @@ * satisfy in order to be supported by this parser (e.g., no left recursion). */ class WP_Parser { - private $grammar; - private $tokens; - private $position; + protected $grammar; + protected $tokens; + protected $position; public function __construct( WP_Parser_Grammar $grammar, array $tokens ) { $this->grammar = $grammar; diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-configurator.php b/wp-includes/sqlite-ast/class-wp-sqlite-configurator.php new file mode 100644 index 0000000..0bf1fcf --- /dev/null +++ b/wp-includes/sqlite-ast/class-wp-sqlite-configurator.php @@ -0,0 +1,124 @@ +driver = $driver; + $this->schema_builder = $schema_builder; + $this->schema_reconstructor = new WP_SQLite_Information_Schema_Reconstructor( + $driver, + $schema_builder + ); + } + + /** + * Ensure that the SQLite database is configured. + * + * This method checks if the database is configured for the latest SQLite + * driver version, and if it is not, it will configure the database. + */ + public function ensure_database_configured(): void { + $version = SQLITE_DRIVER_VERSION; + $db_version = $this->driver->get_saved_driver_version(); + if ( version_compare( $version, $db_version ) > 0 ) { + $this->configure_database(); + } + } + + /** + * Configure the SQLite database. + * + * This method creates tables used for emulating MySQL behaviors in SQLite, + * and populates them with necessary data. When it is used with an already + * configured database, it will update the configuration as per the current + * SQLite driver version and attempt to repair any configuration corruption. + */ + public function configure_database(): void { + // Use an EXCLUSIVE transaction to prevent multiple connections + // from attempting to configure the database at the same time. + $this->driver->execute_sqlite_query( 'BEGIN EXCLUSIVE TRANSACTION' ); + try { + $this->ensure_global_variables_table(); + $this->schema_builder->ensure_information_schema_tables(); + $this->schema_reconstructor->ensure_correct_information_schema(); + $this->save_current_driver_version(); + } catch ( Throwable $e ) { + $this->driver->execute_sqlite_query( 'ROLLBACK' ); + throw $e; + } + $this->driver->execute_sqlite_query( 'COMMIT' ); + } + + /** + * Ensure that the global variables table exists. + * + * This method configures a database table to store MySQL global variables + * and other internal configuration values. + */ + private function ensure_global_variables_table(): void { + $this->driver->execute_sqlite_query( + sprintf( + 'CREATE TABLE IF NOT EXISTS %s (name TEXT PRIMARY KEY, value TEXT)', + WP_SQLite_Driver::GLOBAL_VARIABLES_TABLE_NAME + ) + ); + } + + /** + * Save the current SQLite driver version. + * + * This method saves the current SQLite driver version to the database. + */ + private function save_current_driver_version(): void { + $this->driver->execute_sqlite_query( + sprintf( + 'INSERT INTO %s (name, value) VALUES (?, ?) ON CONFLICT(name) DO UPDATE SET value = ?', + WP_SQLite_Driver::GLOBAL_VARIABLES_TABLE_NAME + ), + array( + WP_SQLite_Driver::DRIVER_VERSION_VARIABLE_NAME, + SQLITE_DRIVER_VERSION, + SQLITE_DRIVER_VERSION, + ) + ); + } +} diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-connection.php b/wp-includes/sqlite-ast/class-wp-sqlite-connection.php new file mode 100644 index 0000000..f79e9b5 --- /dev/null +++ b/wp-includes/sqlite-ast/class-wp-sqlite-connection.php @@ -0,0 +1,198 @@ +pdo = $options['pdo']; + } else { + if ( ! isset( $options['path'] ) || ! is_string( $options['path'] ) ) { + throw new InvalidArgumentException( 'Option "path" is required when "connection" is not provided.' ); + } + $this->pdo = new PDO( 'sqlite:' . $options['path'] ); + } + + // Throw exceptions on error. + $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + + // Configure SQLite timeout. + if ( isset( $options['timeout'] ) && is_int( $options['timeout'] ) ) { + $timeout = $options['timeout']; + } else { + $timeout = self::DEFAULT_SQLITE_TIMEOUT; + } + $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout ); + + // Return all values (except null) as strings. + $this->pdo->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, true ); + + // Configure SQLite journal mode. + $journal_mode = $options['journal_mode'] ?? null; + if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) { + $this->query( 'PRAGMA journal_mode = ' . $journal_mode ); + } + } + + /** + * Execute a query in SQLite. + * + * @param string $sql The query to execute. + * @param array $params The query parameters. + * @throws PDOException When the query execution fails. + * @return PDOStatement The PDO statement object. + */ + public function query( string $sql, array $params = array() ): PDOStatement { + if ( $this->query_logger ) { + ( $this->query_logger )( $sql, $params ); + } + $stmt = $this->pdo->prepare( $sql ); + $stmt->execute( $params ); + return $stmt; + } + + /** + * Returns the ID of the last inserted row. + * + * @return string The ID of the last inserted row. + */ + public function get_last_insert_id(): string { + return $this->pdo->lastInsertId(); + } + + /** + * Quote a value for use in a query. + * + * @param mixed $value The value to quote. + * @param int $type The type of the value. + * @return string The quoted value. + */ + public function quote( $value, int $type = PDO::PARAM_STR ): string { + return $this->pdo->quote( $value, $type ); + } + + /** + * Quote an SQLite identifier. + * + * Wraps the identifier in backticks and escapes backtick characters within. + * + * --- + * + * Quoted identifiers in SQLite are represented by string constants: + * + * A string constant is formed by enclosing the string in single quotes ('). + * A single quote within the string can be encoded by putting two single + * quotes in a row - as in Pascal. C-style escapes using the backslash + * character are not supported because they are not standard SQL. + * + * See: https://www.sqlite.org/lang_expr.html#literal_values_constants_ + * + * Although sparsely documented, this applies to backtick and double quoted + * string constants as well, so only the quote character needs to be escaped. + * + * For more details, see the grammar for SQLite table and column names: + * + * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/tokenize.c#L395-L419 + * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/parse.y#L321-L338 + * + * --- + * + * We use backtick quotes instead of the SQL standard double quotes, due to + * an SQLite quirk causing double-quoted strings to be accepted as literals: + * + * This misfeature means that a misspelled double-quoted identifier will + * be interpreted as a string literal, rather than generating an error. + * + * See: https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted + * + * @param string $unquoted_identifier The unquoted identifier value. + * @return string The quoted identifier value. + */ + public function quote_identifier( string $unquoted_identifier ): string { + return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`'; + } + + /** + * Get the PDO object. + * + * @return PDO + */ + public function get_pdo(): PDO { + return $this->pdo; + } + + /** + * Set a logger for the queries. + * + * @param callable(string, array): void $logger A query logger callback. + */ + public function set_query_logger( callable $logger ): void { + $this->query_logger = $logger; + } +} diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php index 361f278..80b05c9 100644 --- a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php +++ b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php @@ -28,11 +28,6 @@ class WP_SQLite_Driver { */ const MINIMUM_SQLITE_VERSION = '3.37.0'; - /** - * The default timeout in seconds for SQLite to wait for a writable lock. - */ - const DEFAULT_SQLITE_TIMEOUT = 10; - /** * An identifier prefix for internal database objects. * @@ -40,6 +35,22 @@ class WP_SQLite_Driver { */ const RESERVED_PREFIX = '_wp_sqlite_'; + /** + * The name of a global variables table. + * + * This special table is used to emulate MySQL global variables and to store + * some internal configuration values. + */ + const GLOBAL_VARIABLES_TABLE_NAME = self::RESERVED_PREFIX . 'global_variables'; + + /** + * The name of the SQLite driver version variable. + * + * This internal variable is used to store the latest version of the SQLite + * driver that was used to initialize and configure the SQLite database. + */ + const DRIVER_VERSION_VARIABLE_NAME = self::RESERVED_PREFIX . 'driver_version'; + /** * A map of MySQL tokens to SQLite data types. * @@ -326,11 +337,11 @@ class WP_SQLite_Driver { private $db_name; /** - * An instance of the PDO object. + * An instance of the SQLite connection. * - * @var PDO + * @var WP_SQLite_Connection */ - private $pdo; + private $connection; /** * A service for managing MySQL INFORMATION_SCHEMA tables in SQLite. @@ -419,66 +430,15 @@ class WP_SQLite_Driver { * * Set up an SQLite connection and the MySQL-on-SQLite driver. * - * @param array $options { - * An array of options. - * - * @type string $database Database name. - * The name of the emulated MySQL database. - * @type string|null $path Optional. SQLite database path. - * For in-memory database, use ':memory:'. - * Must be set when PDO instance is not provided. - * @type PDO|null $connection Optional. PDO instance with SQLite connection. - * If not provided, a new PDO instance will be created. - * @type int|null $timeout Optional. SQLite timeout in seconds. - * The time to wait for a writable lock. - * @type string|null $sqlite_journal_mode Optional. SQLite journal mode. - * } + * @param WP_SQLite_Connection $connection A SQLite database connection. + * @param string $database The database name. * * @throws WP_SQLite_Driver_Exception When the driver initialization fails. */ - public function __construct( array $options ) { - // Database name. - if ( ! isset( $options['database'] ) || ! is_string( $options['database'] ) ) { - throw $this->new_driver_exception( 'Option "database" is required.' ); - } - $this->main_db_name = $options['database']; - $this->db_name = $this->main_db_name; - - // Database connection. - if ( isset( $options['connection'] ) && $options['connection'] instanceof PDO ) { - $this->pdo = $options['connection']; - } - - // Create a PDO connection if it is not provided. - if ( ! $this->pdo ) { - if ( ! isset( $options['path'] ) || ! is_string( $options['path'] ) ) { - throw $this->new_driver_exception( - 'Option "path" is required when "connection" is not provided.' - ); - } - $path = $options['path']; - - try { - $this->pdo = new PDO( 'sqlite:' . $path ); - } catch ( PDOException $e ) { - $code = $e->getCode(); - throw $this->new_driver_exception( $e->getMessage(), is_int( $code ) ? $code : 0, $e ); - } - } - - // Throw exceptions on error. - $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); - - // Configure SQLite timeout. - if ( isset( $options['timeout'] ) && is_int( $options['timeout'] ) ) { - $timeout = $options['timeout']; - } else { - $timeout = self::DEFAULT_SQLITE_TIMEOUT; - } - $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout ); - - // Return all values (except null) as strings. - $this->pdo->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, true ); + public function __construct( WP_SQLite_Connection $connection, string $database ) { + $this->connection = $connection; + $this->main_db_name = $database; + $this->db_name = $database; // Check the SQLite version. $sqlite_version = $this->get_sqlite_version(); @@ -496,22 +456,10 @@ public function __construct( array $options ) { $this->client_info = $sqlite_version; // Enable foreign keys. By default, they are off. - $this->pdo->query( 'PRAGMA foreign_keys = ON' ); - - // Configure SQLite journal mode. - if ( - isset( $options['sqlite_journal_mode'] ) - && in_array( - $options['sqlite_journal_mode'], - array( 'DELETE', 'TRUNCATE', 'PERSIST', 'MEMORY', 'WAL', 'OFF' ), - true - ) - ) { - $this->pdo->query( 'PRAGMA journal_mode = ' . $options['sqlite_journal_mode'] ); - } + $this->connection->query( 'PRAGMA foreign_keys = ON' ); // Register SQLite functions. - WP_SQLite_PDO_User_Defined_Functions::register_for( $this->pdo ); + WP_SQLite_PDO_User_Defined_Functions::register_for( $this->connection->get_pdo() ); // Load MySQL grammar. if ( null === self::$mysql_grammar ) { @@ -522,18 +470,30 @@ public function __construct( array $options ) { $this->information_schema_builder = new WP_SQLite_Information_Schema_Builder( $this->main_db_name, self::RESERVED_PREFIX, - array( $this, 'execute_sqlite_query' ) + $this->connection + ); + + // Ensure that the database is configured. + $migrator = new WP_SQLite_Configurator( $this, $this->information_schema_builder ); + $migrator->ensure_database_configured(); + + $this->connection->set_query_logger( + function ( string $sql, array $params ) { + $this->last_sqlite_queries[] = array( + 'sql' => $sql, + 'params' => $params, + ); + } ); - $this->information_schema_builder->ensure_information_schema_tables(); } /** - * Get the PDO object. + * Get the SQLite connection instance. * - * @return PDO + * @return WP_SQLite_Connection */ - public function get_pdo(): PDO { - return $this->pdo; + public function get_connection(): WP_SQLite_Connection { + return $this->connection; } /** @@ -542,7 +502,35 @@ public function get_pdo(): PDO { * @return string SQLite engine version as a string. */ public function get_sqlite_version(): string { - return $this->pdo->query( 'SELECT SQLITE_VERSION()' )->fetchColumn(); + return $this->connection->query( 'SELECT SQLITE_VERSION()' )->fetchColumn(); + } + + /** + * Get the SQLite driver version saved in the database. + * + * The saved driver version corresponds to the latest version of the SQLite + * driver that was used to initialize and configure the SQLite database. + * + * @return string SQLite driver version as a string. + * @throws PDOException When the query execution fails. + */ + public function get_saved_driver_version(): string { + $default_version = '0.0.0'; + try { + $stmt = $this->execute_sqlite_query( + sprintf( + 'SELECT value FROM %s WHERE name = ?', + $this->quote_sqlite_identifier( self::GLOBAL_VARIABLES_TABLE_NAME ) + ), + array( self::DRIVER_VERSION_VARIABLE_NAME ) + ); + return $stmt->fetchColumn() ?? $default_version; + } catch ( PDOException $e ) { + if ( str_contains( $e->getMessage(), 'no such table' ) ) { + return $default_version; + } + throw $e; + } } /** @@ -579,7 +567,7 @@ public function get_last_sqlite_queries(): array { * @return int|string */ public function get_insert_id() { - $last_insert_id = $this->pdo->lastInsertId(); + $last_insert_id = $this->connection->get_last_insert_id(); if ( is_numeric( $last_insert_id ) ) { $last_insert_id = (int) $last_insert_id; } @@ -611,16 +599,17 @@ public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mo try { // Parse the MySQL query. - $lexer = new WP_MySQL_Lexer( $query ); - $tokens = $lexer->remaining_tokens(); - - $parser = new WP_MySQL_Parser( self::$mysql_grammar, $tokens ); - $ast = $parser->parse(); - + $parser = $this->create_parser( $query ); + $parser->next_query(); + $ast = $parser->get_query_ast(); if ( null === $ast ) { throw $this->new_driver_exception( 'Failed to parse the MySQL query.' ); } + if ( $parser->next_query() ) { + throw $this->new_driver_exception( 'Multi-query is not supported.' ); + } + // Handle transaction commands. /* @@ -691,6 +680,18 @@ public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mo } } + /** + * Tokenize a MySQL query and initialize a parser. + * + * @param string $query The MySQL query to parse. + * @return WP_MySQL_Parser A parser initialized for the MySQL query. + */ + public function create_parser( string $query ): WP_MySQL_Parser { + $lexer = new WP_MySQL_Lexer( $query ); + $tokens = $lexer->remaining_tokens(); + return new WP_MySQL_Parser( self::$mysql_grammar, $tokens ); + } + /** * Get results of the last query. * @@ -718,13 +719,7 @@ public function get_last_return_value() { * @return PDOStatement The PDO statement object. */ public function execute_sqlite_query( string $sql, array $params = array() ): PDOStatement { - $this->last_sqlite_queries[] = array( - 'sql' => $sql, - 'params' => $params, - ); - $stmt = $this->pdo->prepare( $sql ); - $stmt->execute( $params ); - return $stmt; + return $this->connection->query( $sql, $params ); } /** @@ -1158,7 +1153,11 @@ private function execute_delete_statement( WP_Parser_Node $node ): void { $select_list = array(); foreach ( $table_aliases as $table ) { - $select_list[] = "\"$table\".rowid AS \"{$table}_rowid\""; + $select_list[] = sprintf( + '%s.rowid AS %s', + $this->quote_sqlite_identifier( $table ), + $this->quote_sqlite_identifier( $table . '_rowid' ) + ); } $ids = $this->execute_sqlite_query( @@ -1234,7 +1233,10 @@ private function execute_create_table_statement( WP_Parser_Node $node ): void { if ( $subnode->has_child_node( 'ifNotExists' ) ) { $tables_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'tables' ); $table_exists = $this->execute_sqlite_query( - "SELECT 1 FROM $tables_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT 1 FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $tables_table ) + ), array( $this->db_name, $table_name ) )->fetchColumn(); @@ -1275,7 +1277,10 @@ private function execute_alter_table_statement( WP_Parser_Node $node ): void { // Save all column names from the original table. $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); $column_names = $this->execute_sqlite_query( - "SELECT COLUMN_NAME FROM $columns_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT COLUMN_NAME FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $columns_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_COLUMN ); @@ -1383,9 +1388,9 @@ private function execute_truncate_table_statement( WP_Parser_Node $node ): void $this->translate( $node->get_first_child_node( 'tableRef' ) ) ); - $quoted_table_name = $this->quote_sqlite_identifier( $table_name ); - - $this->execute_sqlite_query( "DELETE FROM $quoted_table_name" ); + $this->execute_sqlite_query( + sprintf( 'DELETE FROM %s', $this->quote_sqlite_identifier( $table_name ) ) + ); try { $this->execute_sqlite_query( 'DELETE FROM sqlite_sequence WHERE name = ?', array( $table_name ) ); } catch ( PDOException $e ) { @@ -1468,7 +1473,7 @@ private function execute_show_statement( WP_Parser_Node $node ): void { sprintf( 'statement type: "%s" > "%s"', $node->rule_name, - $keyword1->value + $keyword1->get_value() ) ); } @@ -1522,7 +1527,7 @@ private function execute_show_index_statement( string $table_name ): void { INDEX_COMMENT AS `Index_comment`, IS_VISIBLE AS `Visible`, EXPRESSION AS `Expression` - FROM ' . $statistics_table . ' + FROM ' . $this->quote_sqlite_identifier( $statistics_table ) . ' WHERE table_schema = ? AND table_name = ? ORDER BY @@ -1566,7 +1571,8 @@ private function execute_show_table_status_statement( WP_Parser_Node $node ): vo ); $table_info = $this->execute_sqlite_query( sprintf( - "SELECT * FROM $tables_tables WHERE table_schema = ? %s", + 'SELECT * FROM %s WHERE table_schema = ? %s', + $this->quote_sqlite_identifier( $tables_tables ), $condition ?? '' ), array( $database ) @@ -1634,7 +1640,8 @@ private function execute_show_tables_statement( WP_Parser_Node $node ): void { ); $table_info = $this->execute_sqlite_query( sprintf( - "SELECT * FROM $table_tables WHERE table_schema = ? %s", + 'SELECT * FROM %s WHERE table_schema = ? %s', + $this->quote_sqlite_identifier( $table_tables ), $condition ?? '' ), array( $database ) @@ -1691,7 +1698,10 @@ private function execute_show_columns_statement( WP_Parser_Node $node ): void { // Check if the table exists. $tables_tables = $this->information_schema_builder->get_table_name( $table_is_temporary, 'tables' ); $table_exists = $this->execute_sqlite_query( - "SELECT 1 FROM $tables_tables WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT 1 FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $tables_tables ) + ), array( $this->db_name, $table_name ) )->fetchColumn(); @@ -1709,10 +1719,11 @@ private function execute_show_columns_statement( WP_Parser_Node $node ): void { } // Fetch column information. - $column_info = $this->execute_sqlite_query( + $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); + $column_info = $this->execute_sqlite_query( sprintf( 'SELECT * FROM %s WHERE table_schema = ? AND table_name = ? %s', - $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ), + $this->quote_sqlite_identifier( $columns_table ), $condition ?? '' ), array( $database, $table_name ) @@ -1754,7 +1765,7 @@ private function execute_describe_statement( WP_Parser_Node $node ): void { $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); $column_info = $this->execute_sqlite_query( - " + ' SELECT column_name AS `Field`, column_type AS `Type`, @@ -1762,10 +1773,10 @@ private function execute_describe_statement( WP_Parser_Node $node ): void { column_key AS `Key`, column_default AS `Default`, extra AS Extra - FROM $columns_table + FROM ' . $this->quote_sqlite_identifier( $columns_table ) . ' WHERE table_schema = ? AND table_name = ? - ", + ', array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_OBJ ); @@ -1961,11 +1972,13 @@ private function execute_administration_statement( WP_Parser_Node $node ): void try { switch ( $first_token->id ) { case WP_MySQL_Lexer::ANALYZE_SYMBOL: - $stmt = $this->execute_sqlite_query( "ANALYZE $quoted_table_name" ); + $stmt = $this->execute_sqlite_query( sprintf( 'ANALYZE %s', $quoted_table_name ) ); $errors = $stmt->fetchAll( PDO::FETCH_COLUMN ); break; case WP_MySQL_Lexer::CHECK_SYMBOL: - $stmt = $this->execute_sqlite_query( "PRAGMA integrity_check($quoted_table_name)" ); + $stmt = $this->execute_sqlite_query( + sprintf( 'PRAGMA integrity_check(%s)', $quoted_table_name ) + ); $errors = $stmt->fetchAll( PDO::FETCH_COLUMN ); if ( 'ok' === $errors[0] ) { array_shift( $errors ); @@ -1988,7 +2001,7 @@ private function execute_administration_statement( WP_Parser_Node $node ): void sprintf( 'statement type: "%s" > "%s"', $node->rule_name, - $first_token->value + $first_token->get_value() ) ); } @@ -2000,7 +2013,7 @@ private function execute_administration_statement( WP_Parser_Node $node ): void } } - $operation = strtolower( $first_token->value ); + $operation = strtolower( $first_token->get_value() ); foreach ( $errors as $error ) { $results[] = (object) array( 'Table' => $this->db_name . '.' . $table_name, @@ -2124,7 +2137,7 @@ private function translate( $node ): ?string { // @TODO: Handle SET and JSON. throw $this->new_not_supported_exception( - sprintf( 'data type: %s', $child->value ) + sprintf( 'data type: %s', $child->get_value() ) ); case 'fromClause': // FROM DUAL is MySQL-specific syntax that means "FROM no tables" @@ -2163,7 +2176,7 @@ private function translate( $node ): ?string { $name = strtolower( $original_name ); $type = $type_token ? $type_token->id : WP_MySQL_Lexer::SESSION_SYMBOL; if ( 'sql_mode' === $name ) { - $value = $this->pdo->quote( implode( ',', $this->active_sql_modes ) ); + $value = $this->connection->quote( implode( ',', $this->active_sql_modes ) ); } else { // When we have no value, it's reasonable to use NULL. $value = 'NULL'; @@ -2182,7 +2195,7 @@ private function translate( $node ): ?string { '%s AS %s', $value, $this->quote_sqlite_identifier( - '@@' . ( $type_token ? "$type_token->value." : '' ) . $original_name + '@@' . ( $type_token ? "{$type_token->get_value()}." : '' ) . $original_name ) ); case 'castType': @@ -2233,7 +2246,7 @@ private function translate_token( WP_MySQL_Token $token ): ?string { */ return null; default: - return $token->value; + return $token->get_value(); } } @@ -2276,8 +2289,8 @@ private function translate_string_literal( WP_Parser_Node $node ): string { /* * 1. Remove bounding quotes. */ - $quote = $token->value[0]; - $value = substr( $token->value, 1, -1 ); + $quote = $token->get_value()[0]; + $value = substr( $token->get_value(), 1, -1 ); /* * 2. Normalize escaping of "%" and "_" characters. @@ -2363,13 +2376,13 @@ private function translate_pure_identifier( WP_Parser_Node $node ): string { $token = $node->get_first_child_token(); if ( WP_MySQL_Lexer::DOUBLE_QUOTED_TEXT === $token->id ) { - $value = substr( $token->value, 1, -1 ); + $value = substr( $token->get_value(), 1, -1 ); $value = str_replace( '""', '"', $value ); } elseif ( WP_MySQL_Lexer::BACK_TICK_QUOTED_ID === $token->id ) { - $value = substr( $token->value, 1, -1 ); + $value = substr( $token->get_value(), 1, -1 ); $value = str_replace( '``', '`', $value ); } else { - $value = $token->value; + $value = $token->get_value(); } return '`' . str_replace( '`', '``', $value ) . '`'; @@ -2788,7 +2801,10 @@ private function recreate_table_from_information_schema( if ( null === $column_map ) { $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); $column_names = $this->execute_sqlite_query( - "SELECT COLUMN_NAME FROM $columns_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT COLUMN_NAME FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $columns_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_COLUMN ); $column_map = array_combine( $column_names, $column_names ); @@ -2943,13 +2959,13 @@ private function translate_insert_or_replace_body_in_non_strict_mode( $is_temporary = $this->information_schema_builder->temporary_table_exists( $table_name ); $columns_table = $this->information_schema_builder->get_table_name( $is_temporary, 'columns' ); $columns = $this->execute_sqlite_query( - " + ' SELECT column_name, is_nullable, column_default, data_type, extra - FROM $columns_table + FROM ' . $this->quote_sqlite_identifier( $columns_table ) . ' WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position - ", + ', array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); @@ -3011,7 +3027,7 @@ private function translate_insert_or_replace_body_in_non_strict_mode( if ( null === $default && ! $is_nullable && ! $is_auto_inc ) { $default = self::DATA_TYPE_IMPLICIT_DEFAULT_MAP[ $column['DATA_TYPE'] ] ?? null; } - $fragment .= null === $default ? 'NULL' : $this->pdo->quote( $default ); + $fragment .= null === $default ? 'NULL' : $this->connection->quote( $default ); } else { // When a column value is included, we can use it without change. $position = array_search( $column['COLUMN_NAME'], $insert_list, true ); @@ -3059,12 +3075,12 @@ private function translate_update_list_in_non_strict_mode( string $table_name, W $is_temporary = $this->information_schema_builder->temporary_table_exists( $table_name ); $columns_table = $this->information_schema_builder->get_table_name( $is_temporary, 'columns' ); $columns = $this->execute_sqlite_query( - " + ' SELECT column_name, is_nullable, data_type, column_default - FROM $columns_table + FROM ' . $this->quote_sqlite_identifier( $columns_table ) . ' WHERE table_schema = ? AND table_name = ? - ", + ', array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); $column_map = array_combine( array_column( $columns, 'COLUMN_NAME' ), $columns ); @@ -3085,7 +3101,7 @@ private function translate_update_list_in_non_strict_mode( string $table_name, W // Get the UPDATE value. It's either an expression or a DEFAULT keyword. if ( null === $expr ) { // Emulate "column = DEFAULT". - $value = null === $default ? 'NULL' : $this->pdo->quote( $default ); + $value = null === $default ? 'NULL' : $this->connection->quote( $default ); } else { $value = $this->translate( $expr ); } @@ -3093,7 +3109,7 @@ private function translate_update_list_in_non_strict_mode( string $table_name, W // If the column is NOT NULL, a NULL value resolves to implicit default. $implicit_default = self::DATA_TYPE_IMPLICIT_DEFAULT_MAP[ $data_type ] ?? null; if ( ! $is_nullable && null !== $implicit_default ) { - $value = sprintf( 'COALESCE(%s, %s)', $value, $this->pdo->quote( $implicit_default ) ); + $value = sprintf( 'COALESCE(%s, %s)', $value, $this->connection->quote( $implicit_default ) ); } // Compose the UPDATE list item. @@ -3122,9 +3138,9 @@ private function get_sqlite_create_table_statement( // 1. Get table info. $tables_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'tables' ); $table_info = $this->execute_sqlite_query( - " + ' SELECT * - FROM $tables_table + FROM ' . $this->quote_sqlite_identifier( $tables_table ) . " WHERE table_type = 'BASE TABLE' AND table_schema = ? AND table_name = ? @@ -3142,14 +3158,20 @@ private function get_sqlite_create_table_statement( // 2. Get column info. $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); $column_info = $this->execute_sqlite_query( - "SELECT * FROM $columns_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT * FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $columns_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); // 3. Get index info, grouped by index name. $statistics_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'statistics' ); $constraint_info = $this->execute_sqlite_query( - "SELECT * FROM $statistics_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT * FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $statistics_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); @@ -3220,7 +3242,7 @@ private function get_sqlite_create_table_statement( ) { $query .= ' DEFAULT CURRENT_TIMESTAMP'; } else { - $query .= ' DEFAULT ' . $this->pdo->quote( $column['COLUMN_DEFAULT'] ); + $query .= ' DEFAULT ' . $this->connection->quote( $column['COLUMN_DEFAULT'] ); } } $rows[] = $query; @@ -3312,9 +3334,9 @@ private function get_mysql_create_table_statement( bool $table_is_temporary, str // 1. Get table info. $tables_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'tables' ); $table_info = $this->execute_sqlite_query( - " + ' SELECT * - FROM $tables_table + FROM ' . $this->quote_sqlite_identifier( $tables_table ) . " WHERE table_type = 'BASE TABLE' AND table_schema = ? AND table_name = ? @@ -3329,14 +3351,20 @@ private function get_mysql_create_table_statement( bool $table_is_temporary, str // 2. Get column info. $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); $column_info = $this->execute_sqlite_query( - "SELECT * FROM $columns_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT * FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $columns_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); // 3. Get index info, grouped by index name. $statistics_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'statistics' ); $constraint_info = $this->execute_sqlite_query( - "SELECT * FROM $statistics_table WHERE table_schema = ? AND table_name = ?", + sprintf( + 'SELECT * FROM %s WHERE table_schema = ? AND table_name = ?', + $this->quote_sqlite_identifier( $statistics_table ) + ), array( $this->db_name, $table_name ) )->fetchAll( PDO::FETCH_ASSOC ); @@ -3371,7 +3399,7 @@ private function get_mysql_create_table_statement( bool $table_is_temporary, str ) { $sql .= ' DEFAULT CURRENT_TIMESTAMP'; } elseif ( null !== $column['COLUMN_DEFAULT'] ) { - $sql .= ' DEFAULT ' . $this->pdo->quote( $column['COLUMN_DEFAULT'] ); + $sql .= ' DEFAULT ' . $this->connection->quote( $column['COLUMN_DEFAULT'] ); } elseif ( 'YES' === $column['IS_NULLABLE'] ) { $sql .= ' DEFAULT NULL'; } @@ -3405,7 +3433,12 @@ function ( $column ) { } else { $is_unique = '0' === $info['NON_UNIQUE']; - $sql = sprintf( ' %sKEY ', $is_unique ? 'UNIQUE ' : '' ); + $sql = sprintf( + ' %s%s%sKEY ', + $is_unique ? 'UNIQUE ' : '', + 'FULLTEXT' === $info['INDEX_TYPE'] ? 'FULLTEXT ' : '', + 'SPATIAL' === $info['INDEX_TYPE'] ? 'SPATIAL ' : '' + ); $sql .= $this->quote_mysql_identifier( $info['INDEX_NAME'] ); $sql .= ' ('; $sql .= implode( @@ -3459,14 +3492,20 @@ private function get_column_on_update_trigger_query( string $table, string $colu // but currently that can't happen as we're not creating such tables. // See: https://www.sqlite.org/rowidtable.html $trigger_name = self::RESERVED_PREFIX . "{$table}_{$column}_on_update"; - return " - CREATE TRIGGER \"$trigger_name\" - AFTER UPDATE ON \"$table\" - FOR EACH ROW - BEGIN - UPDATE \"$table\" SET \"$column\" = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid; - END - "; + return sprintf( + ' + CREATE TRIGGER %s + AFTER UPDATE ON %s + FOR EACH ROW + BEGIN + UPDATE %s SET %s = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid; + END + ', + $this->quote_sqlite_identifier( $trigger_name ), + $this->quote_sqlite_identifier( $table ), + $this->quote_sqlite_identifier( $table ), + $this->quote_sqlite_identifier( $column ) + ); } /** @@ -3481,22 +3520,19 @@ private function unquote_sqlite_identifier( string $quoted_identifier ): string $first_byte = $quoted_identifier[0] ?? null; if ( '"' === $first_byte || '`' === $first_byte ) { $unquoted = substr( $quoted_identifier, 1, -1 ); - } else { - $unquoted = $quoted_identifier; + return str_replace( $first_byte . $first_byte, $first_byte, $unquoted ); } - return str_replace( $first_byte . $first_byte, $first_byte, $unquoted ); + return $quoted_identifier; } /** * Quote an SQLite identifier. * - * Wrap the identifier in backticks and escape backtick values within. - * * @param string $unquoted_identifier The unquoted identifier value. * @return string The quoted identifier value. */ private function quote_sqlite_identifier( string $unquoted_identifier ): string { - return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`'; + return $this->connection->quote_identifier( $unquoted_identifier ); } diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php b/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php index 4f8cc96..1ba51dc 100644 --- a/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php +++ b/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php @@ -358,25 +358,22 @@ class WP_SQLite_Information_Schema_Builder { private $temporary_information_schema_exists = false; /** - * Query callback. + * An instance of the SQLite connection. * - * @TODO: Consider extracting a part of the WP_SQLite_Driver class - * to a class like "WP_SQLite_Connection" and reuse it in both. - * - * @var callable(string, array): PDOStatement + * @var WP_SQLite_Connection */ - private $query_callback; + private $connection; /** * Constructor. * - * @param string $database Database name. - * @param string $reserved_prefix An identifier prefix for internal database objects. - * @param callable(string, array): PDOStatement $query_callback A callback that executes an SQLite query. + * @param string $database Database name. + * @param string $reserved_prefix An identifier prefix for internal database objects. + * @param WP_SQLite_Connection $connection An instance of the SQLite connection. */ - public function __construct( string $database, string $reserved_prefix, callable $query_callback ) { + public function __construct( string $database, string $reserved_prefix, WP_SQLite_Connection $connection ) { $this->db_name = $database; - $this->query_callback = $query_callback; + $this->connection = $connection; $this->table_prefix = $reserved_prefix . 'mysql_information_schema_'; $this->temporary_table_prefix = $reserved_prefix . 'mysql_information_schema_tmp_'; } @@ -404,7 +401,7 @@ public function temporary_table_exists( string $table_name ): bool { * We could search in the "{$this->temporary_table_prefix}tables" table, * but it may not exist yet, so using "sqlite_temp_schema" is simpler. */ - $stmt = $this->query( + $stmt = $this->connection->query( "SELECT 1 FROM sqlite_temp_schema WHERE type = 'table' AND name = ?", array( $table_name ) ); @@ -417,7 +414,7 @@ public function temporary_table_exists( string $table_name ): bool { */ public function ensure_information_schema_tables(): void { foreach ( self::CREATE_INFORMATION_SCHEMA_QUERIES as $query ) { - $this->query( str_replace( '', $this->table_prefix, $query ) ); + $this->connection->query( str_replace( '', $this->table_prefix, $query ) ); } } @@ -428,7 +425,7 @@ public function ensure_information_schema_tables(): void { public function ensure_temporary_information_schema_tables(): void { foreach ( self::CREATE_INFORMATION_SCHEMA_QUERIES as $query ) { $query = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $query ); - $this->query( str_replace( '', $this->temporary_table_prefix, $query ) ); + $this->connection->query( str_replace( '', $this->temporary_table_prefix, $query ) ); } $this->temporary_information_schema_exists = true; } @@ -568,7 +565,7 @@ public function record_alter_table( WP_Parser_Node $node ): void { continue; } - throw new \Exception( sprintf( 'Unsupported ALTER TABLE ADD action: %s', $first_token->value ) ); + throw new \Exception( sprintf( 'Unsupported ALTER TABLE ADD action: %s', $first_token->get_value() ) ); } // CHANGE [COLUMN] @@ -673,13 +670,13 @@ private function record_add_column( WP_Parser_Node $node ): void { $columns_table_name = $this->get_table_name( $table_is_temporary, 'columns' ); - $position = $this->query( - " + $position = $this->connection->query( + ' SELECT MAX(ordinal_position) - FROM $columns_table_name + FROM ' . $this->connection->quote_identifier( $columns_table_name ) . ' WHERE table_schema = ? AND table_name = ? - ", + ', array( $this->db_name, $table_name ) )->fetchColumn(); @@ -893,13 +890,13 @@ private function record_add_constraint( $column_names = array_filter( $key_part_column_names ); if ( count( $column_names ) > 0 ) { $columns_table_name = $this->get_table_name( $table_is_temporary, 'columns' ); - $column_info = $this->query( - " + $column_info = $this->connection->query( + ' SELECT column_name, data_type, is_nullable, character_maximum_length - FROM $columns_table_name + FROM ' . $this->connection->quote_identifier( $columns_table_name ) . ' WHERE table_schema = ? AND table_name = ? - AND column_name IN (" . implode( ',', array_fill( 0, count( $column_names ), '?' ) ) . ') + AND column_name IN (' . implode( ',', array_fill( 0, count( $column_names ), '?' ) ) . ') ', array_merge( array( $this->db_name, $table_name ), $column_names ) )->fetchAll( @@ -1078,7 +1075,7 @@ private function sync_column_key_info( bool $table_is_temporary, string $table_n // @TODO: Consider listing only affected columns. $columns_table_name = $this->get_table_name( $table_is_temporary, 'columns' ); $statistics_table_name = $this->get_table_name( $table_is_temporary, 'statistics' ); - $this->query( + $this->connection->query( " WITH s AS ( SELECT @@ -1089,12 +1086,12 @@ private function sync_column_key_info( bool $table_is_temporary, string $table_n WHEN MAX(seq_in_index = 1) THEN 'MUL' ELSE '' END AS column_key - FROM $statistics_table_name + FROM " . $this->connection->quote_identifier( $statistics_table_name ) . ' WHERE table_schema = ? AND table_name = ? GROUP BY column_name ) - UPDATE $columns_table_name AS c + UPDATE ' . $this->connection->quote_identifier( $columns_table_name ) . " AS c SET column_key = s.column_key, is_nullable = IIF(s.column_key = 'PRI', 'NO', c.is_nullable) @@ -1353,7 +1350,7 @@ private function get_column_data_types( WP_Parser_Node $node ): array { ) { $type = 'mediumtext'; } else { - throw new \RuntimeException( 'Unknown data type: ' . $token->value ); + throw new \RuntimeException( 'Unknown data type: ' . $token->get_value() ); } // Get full type. @@ -1619,8 +1616,8 @@ private function get_column_numeric_attributes( WP_Parser_Node $node, string $da $precision_node = $node->get_first_descendant_node( 'precision' ); if ( null !== $precision_node ) { $values = $precision_node->get_descendant_tokens( WP_MySQL_Lexer::INT_NUMBER ); - $precision = (int) $values[0]->value; - $scale = (int) $values[1]->value; + $precision = (int) $values[0]->get_value(); + $scale = (int) $values[1]->get_value(); } if ( 'float' === $data_type ) { @@ -1865,20 +1862,20 @@ private function get_value( WP_Parser_Node $node ): string { if ( $child instanceof WP_Parser_Node ) { $value = $this->get_value( $child ); } elseif ( WP_MySQL_Lexer::BACK_TICK_QUOTED_ID === $child->id ) { - $value = substr( $child->value, 1, -1 ); + $value = substr( $child->get_value(), 1, -1 ); $value = str_replace( '``', '`', $value ); } elseif ( WP_MySQL_Lexer::SINGLE_QUOTED_TEXT === $child->id ) { - $value = $child->value; + $value = $child->get_value(); $value = substr( $value, 1, -1 ); $value = str_replace( '\"', '"', $value ); $value = str_replace( '""', '"', $value ); } elseif ( WP_MySQL_Lexer::DOUBLE_QUOTED_TEXT === $child->id ) { - $value = $child->value; + $value = $child->get_value(); $value = substr( $value, 1, -1 ); $value = str_replace( '\"', '"', $value ); $value = str_replace( '""', '"', $value ); } else { - $value = $child->value; + $value = $child->get_value(); } $full_value .= $value; } @@ -1892,11 +1889,18 @@ private function get_value( WP_Parser_Node $node ): string { * @param array $data The data to insert (key is column name, value is column value). */ private function insert_values( string $table_name, array $data ): void { - $this->query( - ' - INSERT INTO ' . $table_name . ' (' . implode( ', ', array_keys( $data ) ) . ') - VALUES (' . implode( ', ', array_fill( 0, count( $data ), '?' ) ) . ') - ', + $insert_columns = array(); + foreach ( $data as $column => $value ) { + $insert_columns[] = $this->connection->quote_identifier( $column ); + } + + $this->connection->query( + sprintf( + 'INSERT INTO %s (%s) VALUES (%s)', + $this->connection->quote_identifier( $table_name ), + implode( ', ', $insert_columns ), + implode( ', ', array_fill( 0, count( $data ), '?' ) ) + ), array_values( $data ) ); } @@ -1909,22 +1913,23 @@ private function insert_values( string $table_name, array $data ): void { * @param array $where The WHERE clause conditions (key is column name, value is column value). */ private function update_values( string $table_name, array $data, array $where ): void { - $set = array(); + $set_statements = array(); foreach ( $data as $column => $value ) { - $set[] = $column . ' = ?'; + $set_statements[] = $this->connection->quote_identifier( $column ) . ' = ?'; } - $where_clause = array(); + $where_statements = array(); foreach ( $where as $column => $value ) { - $where_clause[] = $column . ' = ?'; + $where_statements[] = $this->connection->quote_identifier( $column ) . ' = ?'; } - $this->query( - ' - UPDATE ' . $table_name . ' - SET ' . implode( ', ', $set ) . ' - WHERE ' . implode( ' AND ', $where_clause ) . ' - ', + $this->connection->query( + sprintf( + 'UPDATE %s SET %s WHERE %s', + $this->connection->quote_identifier( $table_name ), + implode( ', ', $set_statements ), + implode( ' AND ', $where_statements ) + ), array_merge( array_values( $data ), array_values( $where ) ) ); } @@ -1936,29 +1941,18 @@ private function update_values( string $table_name, array $data, array $where ): * @param array $where The WHERE clause conditions (key is column name, value is column value). */ private function delete_values( string $table_name, array $where ): void { - $where_clause = array(); + $where_statements = array(); foreach ( $where as $column => $value ) { - $where_clause[] = $column . ' = ?'; + $where_statements[] = $this->connection->quote_identifier( $column ) . ' = ?'; } - $this->query( - ' - DELETE FROM ' . $table_name . ' - WHERE ' . implode( ' AND ', $where_clause ) . ' - ', + $this->connection->query( + sprintf( + 'DELETE FROM %s WHERE %s', + $this->connection->quote_identifier( $table_name ), + implode( ' AND ', $where_statements ) + ), array_values( $where ) ); } - - /** - * Execute an SQLite query. - * - * @param string $query The query to execute. - * @param array $params The query parameters. - * - * @return PDOStatement - */ - private function query( string $query, array $params = array() ) { - return ( $this->query_callback )( $query, $params ); - } } diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php b/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php new file mode 100644 index 0000000..3cab7b1 --- /dev/null +++ b/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php @@ -0,0 +1,671 @@ +driver = $driver; + $this->connection = $driver->get_connection(); + $this->schema_builder = $schema_builder; + } + + /** + * Ensure that the MySQL INFORMATION_SCHEMA data in SQLite is correct. + * + * This method checks if the MySQL INFORMATION_SCHEMA data in SQLite is correct, + * and if it is not, it will reconstruct missing data and remove stale values. + */ + public function ensure_correct_information_schema(): void { + $sqlite_tables = $this->get_sqlite_table_names(); + $information_schema_tables = $this->get_information_schema_table_names(); + + // In WordPress, use "wp_get_db_schema()" to reconstruct WordPress tables. + $wp_tables = $this->get_wp_create_table_statements(); + + // Reconstruct information schema records for tables that don't have them. + foreach ( $sqlite_tables as $table ) { + if ( ! in_array( $table, $information_schema_tables, true ) ) { + if ( isset( $wp_tables[ $table ] ) ) { + // WordPress core table (as returned by "wp_get_db_schema()"). + $ast = $wp_tables[ $table ]; + } else { + // Other table (a WordPress plugin or unrelated to WordPress). + $sql = $this->generate_create_table_statement( $table ); + $ast = $this->driver->create_parser( $sql )->parse(); + if ( null === $ast ) { + throw new WP_SQLite_Driver_Exception( $this->driver, 'Failed to parse the MySQL query.' ); + } + } + $this->schema_builder->record_create_table( $ast ); + } + } + + // Remove information schema records for tables that don't exist. + foreach ( $information_schema_tables as $table ) { + if ( ! in_array( $table, $sqlite_tables, true ) ) { + $sql = sprintf( 'DROP TABLE %s', $this->connection->quote_identifier( $table ) ); // TODO: mysql quote + $ast = $this->driver->create_parser( $sql )->parse(); + if ( null === $ast ) { + throw new WP_SQLite_Driver_Exception( $this->driver, 'Failed to parse the MySQL query.' ); + } + $this->schema_builder->record_drop_table( + $ast->get_first_descendant_node( 'dropStatement' ) + ); + } + } + } + + /** + * Get the names of all existing tables in the SQLite database. + * + * @return string[] The names of tables in the SQLite database. + */ + private function get_sqlite_table_names(): array { + return $this->driver->execute_sqlite_query( + " + SELECT name + FROM sqlite_schema + WHERE type = 'table' + AND name != ? + AND name NOT LIKE ? ESCAPE '\' + AND name NOT LIKE ? ESCAPE '\' + ORDER BY name + ", + array( + '_mysql_data_types_cache', + 'sqlite\_%', + str_replace( '_', '\_', WP_SQLite_Driver::RESERVED_PREFIX ) . '%', + ) + )->fetchAll( PDO::FETCH_COLUMN ); + } + + /** + * Get the names of all tables recorded in the information schema. + * + * @return string[] The names of tables in the information schema. + */ + private function get_information_schema_table_names(): array { + $tables_table = $this->schema_builder->get_table_name( false, 'tables' ); + return $this->driver->execute_sqlite_query( + sprintf( + 'SELECT table_name FROM %s ORDER BY table_name', + $this->connection->quote_identifier( $tables_table ) + ) + )->fetchAll( PDO::FETCH_COLUMN ); + } + + /** + * Get a map of parsed CREATE TABLE statements for WordPress tables. + * + * When reconstructing the information schema data for WordPress tables, we + * can use the "wp_get_db_schema()" function to get accurate CREATE TABLE + * statements. This method parses the result of "wp_get_db_schema()" into + * an array of parsed CREATE TABLE statements indexed by the table names. + * + * @return array The WordPress CREATE TABLE statements. + */ + private function get_wp_create_table_statements(): array { + if ( ! defined( 'ABSPATH' ) ) { + return array(); + } + if ( file_exists( ABSPATH . 'wp-admin/includes/schema.php' ) ) { + require_once ABSPATH . 'wp-admin/includes/schema.php'; + } + if ( ! function_exists( 'wp_get_db_schema' ) ) { + throw new Exception( 'The "wp_get_db_schema()" function was not defined.' ); + } + + /* + * At this point, WPDB may not yet be initialized, as we're configuring + * the database connection. Let's only populate the table names using + * the "$table_prefix" global so we can get correct table names. + */ + global $wpdb, $table_prefix; + $wpdb->set_prefix( $table_prefix ); + + // Get schema for global tables. + $schema = wp_get_db_schema( 'global' ); + + // For multisite installs, add schema definitions for all sites. + if ( is_multisite() ) { + /* + * We need to use a database query over the "get_sites()" function, + * as WPDB may not yet initialized. Moreover, we need to get the IDs + * of all existing blogs, independent of any filters and actions that + * could possibly alter the results of a "get_sites()" call. + */ + $blog_ids = $this->driver->execute_sqlite_query( + sprintf( + 'SELECT blog_id FROM %s', + $this->connection->quote_identifier( $wpdb->blogs ) + ) + )->fetchAll( PDO::FETCH_COLUMN ); + foreach ( $blog_ids as $blog_id ) { + $schema .= wp_get_db_schema( 'blog', (int) $blog_id ); + } + } else { + // For single site installs, add schema for the main site. + $schema .= wp_get_db_schema( 'blog' ); + } + + // Parse the schema. + $parser = $this->driver->create_parser( $schema ); + $wp_tables = array(); + while ( $parser->next_query() ) { + $ast = $parser->get_query_ast(); + if ( null === $ast ) { + throw new WP_SQLite_Driver_Exception( $this->driver, 'Failed to parse the MySQL query.' ); + } + + $create_node = $ast->get_first_descendant_node( 'createStatement' ); + if ( $create_node && $create_node->has_child_node( 'createTable' ) ) { + $name_node = $create_node->get_first_descendant_node( 'tableName' ); + $name = $this->unquote_mysql_identifier( + substr( $schema, $name_node->get_start(), $name_node->get_length() ) + ); + + $wp_tables[ $name ] = $create_node; + } + } + return $wp_tables; + } + + /** + * Generate a MySQL CREATE TABLE statement from an SQLite table definition. + * + * @param string $table_name The name of the table. + * @return string The CREATE TABLE statement. + */ + private function generate_create_table_statement( string $table_name ): string { + // Columns. + $columns = $this->driver->execute_sqlite_query( + sprintf( + 'PRAGMA table_xinfo(%s)', + $this->connection->quote_identifier( $table_name ) + ) + )->fetchAll( PDO::FETCH_ASSOC ); + + $definitions = array(); + $column_types = array(); + foreach ( $columns as $column ) { + $mysql_type = $this->get_cached_mysql_data_type( $table_name, $column['name'] ); + if ( null === $mysql_type ) { + $mysql_type = $this->get_mysql_column_type( $column['type'] ); + } + $definitions[] = $this->generate_column_definition( $table_name, $column ); + $column_types[ $column['name'] ] = $mysql_type; + } + + // Primary key. + $pk_columns = array(); + foreach ( $columns as $column ) { + // A position of the column in the primary key, starting from index 1. + // A value of 0 means that the column is not part of the primary key. + $pk_position = (int) $column['pk']; + if ( 0 !== $pk_position ) { + $pk_columns[ $pk_position ] = $column['name']; + } + } + + // Sort the columns by their position in the primary key. + ksort( $pk_columns ); + + if ( count( $pk_columns ) > 0 ) { + $quoted_pk_columns = array(); + foreach ( $pk_columns as $pk_column ) { + $quoted_pk_columns[] = $this->connection->quote_identifier( $pk_column ); + } + $definitions[] = sprintf( 'PRIMARY KEY (%s)', implode( ', ', $quoted_pk_columns ) ); + } + + // Indexes and keys. + $keys = $this->driver->execute_sqlite_query( + sprintf( + 'PRAGMA index_list(%s)', + $this->connection->quote_identifier( $table_name ) + ) + )->fetchAll( PDO::FETCH_ASSOC ); + + foreach ( $keys as $key ) { + // Skip the internal index that SQLite may create for a primary key. + // In MySQL, no explicit index needs to be defined for a primary key. + if ( 'pk' === $key['origin'] ) { + continue; + } + $definitions[] = $this->generate_key_definition( $table_name, $key, $column_types ); + } + + return sprintf( + "CREATE TABLE %s (\n %s\n)", + $this->connection->quote_identifier( $table_name ), + implode( ",\n ", $definitions ) + ); + } + + /** + * Generate a MySQL column definition from an SQLite column information. + * + * This method generates a MySQL column definition from SQLite column data. + * + * @param string $table_name The name of the table. + * @param array $column_info The SQLite column information. + * @return string The MySQL column definition. + */ + private function generate_column_definition( string $table_name, array $column_info ): string { + $definition = array(); + $definition[] = $this->connection->quote_identifier( $column_info['name'] ); + + // Data type. + $mysql_type = $this->get_cached_mysql_data_type( $table_name, $column_info['name'] ); + if ( null === $mysql_type ) { + $mysql_type = $this->get_mysql_column_type( $column_info['type'] ); + } + + /** + * Correct some column types based on their default values: + * 1. In MySQL, non-datetime columns can't have a timestamp default. + * Let's use DATETIME when default is set to CURRENT_TIMESTAMP. + * 2. In MySQL, TEXT and BLOB columns can't have a default value. + * Let's use VARCHAR(65535) and VARBINARY(65535) when default is set. + */ + $default = $this->generate_column_default( $mysql_type, $column_info['dflt_value'] ); + if ( 'CURRENT_TIMESTAMP' === $default ) { + $mysql_type = 'datetime'; + } elseif ( 'text' === $mysql_type && null !== $default ) { + $mysql_type = 'varchar(65535)'; + } elseif ( 'blob' === $mysql_type && null !== $default ) { + $mysql_type = 'varbinary(65535)'; + } + + $definition[] = $mysql_type; + + // NULL/NOT NULL. + if ( '1' === $column_info['notnull'] ) { + $definition[] = 'NOT NULL'; + } + + // Auto increment. + $is_auto_increment = false; + if ( '0' !== $column_info['pk'] ) { + $is_auto_increment = $this->driver->execute_sqlite_query( + 'SELECT 1 FROM sqlite_schema WHERE tbl_name = ? AND sql LIKE ?', + array( $table_name, '%AUTOINCREMENT%' ) + )->fetchColumn(); + + if ( $is_auto_increment ) { + $definition[] = 'AUTO_INCREMENT'; + } + } + + // Default value. + if ( null !== $default && ! $is_auto_increment ) { + $definition[] = 'DEFAULT ' . $default; + } + + return implode( ' ', $definition ); + } + + /** + * Generate a MySQL key definition from an SQLite key information. + * + * This method generates a MySQL key definition from SQLite key data. + * + * @param string $table_name The name of the table. + * @param array $key_info The SQLite key information. + * @param array $column_types The MySQL data types of the columns. + * @return string The MySQL key definition. + */ + private function generate_key_definition( string $table_name, array $key_info, array $column_types ): string { + $definition = array(); + + // Key type. + $cached_type = $this->get_cached_mysql_data_type( $table_name, $key_info['name'] ); + if ( 'FULLTEXT' === $cached_type ) { + $definition[] = 'FULLTEXT KEY'; + } elseif ( 'SPATIAL' === $cached_type ) { + $definition[] = 'SPATIAL KEY'; + } elseif ( 'UNIQUE' === $cached_type || '1' === $key_info['unique'] ) { + $definition[] = 'UNIQUE KEY'; + } else { + $definition[] = 'KEY'; + } + + // Key name. + $name = $key_info['name']; + + /* + * The SQLite driver prefixes index names with "{$table_name}__" to avoid + * naming conflicts among tables in SQLite. We need to remove the prefix. + */ + if ( str_starts_with( $name, "{$table_name}__" ) ) { + $name = substr( $name, strlen( "{$table_name}__" ) ); + } + + /** + * SQLite creates automatic internal indexes for primary and unique keys, + * naming them in format "sqlite_autoindex_{$table_name}_{$index_id}". + * For these internal indexes, we need to skip their name, so that in + * the generated MySQL definition, they follow implicit MySQL naming. + */ + if ( ! str_starts_with( $name, 'sqlite_autoindex_' ) ) { + $definition[] = $this->connection->quote_identifier( $name ); + } + + // Key columns. + $key_columns = $this->driver->execute_sqlite_query( + sprintf( + 'PRAGMA index_info(%s)', + $this->connection->quote_identifier( $key_info['name'] ) + ) + )->fetchAll( PDO::FETCH_ASSOC ); + $cols = array(); + foreach ( $key_columns as $column ) { + /* + * Extract type and length from column data type definition. + * + * This is required when the column data type is inferred from the + * '_mysql_data_types_cache' table, which stores the data type in + * the format "type(length)", such as "varchar(255)". + */ + $max_prefix_length = 100; + $type = strtolower( $column_types[ $column['name'] ] ); + $parts = explode( '(', $type ); + $column_type = $parts[0]; + $column_length = isset( $parts[1] ) ? (int) $parts[1] : null; + + /* + * Add an index column prefix length, if needed. + * + * This is required for "text" and "blob" types for columns inferred + * directly from the SQLite schema, and for the following types for + * columns inferred from the '_mysql_data_types_cache' table: + * char, varchar + * text, tinytext, mediumtext, longtext + * blob, tinyblob, mediumblob, longblob + * varbinary + */ + if ( + str_ends_with( $column_type, 'char' ) + || str_ends_with( $column_type, 'text' ) + || str_ends_with( $column_type, 'blob' ) + || str_starts_with( $column_type, 'var' ) + ) { + $cols[] = sprintf( + '%s(%d)', + $this->connection->quote_identifier( $column['name'] ), + min( $column_length ?? $max_prefix_length, $max_prefix_length ) + ); + } else { + $cols[] = $this->connection->quote_identifier( $column['name'] ); + } + } + + $definition[] = '(' . implode( ', ', $cols ) . ')'; + return implode( ' ', $definition ); + } + + /** + * Generate a MySQL default value from an SQLite default value. + * + * @param string $mysql_type The MySQL data type of the column. + * @param string|null $default_value The default value of the SQLite column. + * @return string|null The default value, or null if the column has no default value. + */ + private function generate_column_default( string $mysql_type, ?string $default_value ): ?string { + if ( null === $default_value || '' === $default_value ) { + return null; + } + $mysql_type = strtolower( $mysql_type ); + + /* + * In MySQL, geometry columns can't have a default value. + * + * Geometry columns are saved as TEXT in SQLite, and in an older version + * of the SQLite driver, TEXT columns were assigned a default value of ''. + */ + if ( 'geomcollection' === $mysql_type || 'geometrycollection' === $mysql_type ) { + return null; + } + + /* + * In MySQL, date/time columns can't have a default value of ''. + * + * Date/time columns are saved as TEXT in SQLite, and in an older version + * of the SQLite driver, TEXT columns were assigned a default value of ''. + */ + if ( + "''" === $default_value + && in_array( $mysql_type, array( 'datetime', 'date', 'time', 'timestamp', 'year' ), true ) + ) { + return null; + } + + /** + * Convert SQLite default values to MySQL default values. + * + * See: + * - https://www.sqlite.org/syntax/column-constraint.html + * - https://www.sqlite.org/syntax/literal-value.html + * - https://www.sqlite.org/lang_expr.html#literal_values_constants_ + */ + + // Quoted string literal. E.g.: 'abc', "abc", `abc` + $first_byte = $default_value[0] ?? null; + if ( '"' === $first_byte || "'" === $first_byte || '`' === $first_byte ) { + return $this->escape_mysql_string_literal( substr( $default_value, 1, -1 ) ); + } + + // Normalize the default value to for easier comparison. + $uppercase_default_value = strtoupper( $default_value ); + + // NULL, TRUE, FALSE. + if ( 'NULL' === $uppercase_default_value ) { + // DEFAULT NULL is the same as no default value. + return null; + } elseif ( 'TRUE' === $uppercase_default_value ) { + return '1'; + } elseif ( 'FALSE' === $uppercase_default_value ) { + return '0'; + } + + // Date/time values. + if ( 'CURRENT_TIMESTAMP' === $uppercase_default_value ) { + return 'CURRENT_TIMESTAMP'; + } elseif ( 'CURRENT_DATE' === $uppercase_default_value ) { + return null; // Not supported in MySQL. + } elseif ( 'CURRENT_TIME' === $uppercase_default_value ) { + return null; // Not supported in MySQL. + } + + // SQLite supports underscores in all numeric literals. + $no_underscore_default_value = str_replace( '_', '', $default_value ); + + // Numeric literals. E.g.: 123, 1.23, -1.23, 1e3, 1.2e-3 + if ( is_numeric( $no_underscore_default_value ) ) { + return $no_underscore_default_value; + } + + // HEX literals (numeric). E.g.: 0x1a2f, 0X1A2F + $value = filter_var( $no_underscore_default_value, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX ); + if ( false !== $value ) { + return $value; + } + + // BLOB literals (string). E.g.: x'1a2f', X'1A2F' + // Checking the prefix is enough as SQLite doesn't allow malformed values. + if ( str_starts_with( $uppercase_default_value, "X'" ) ) { + // Convert the hex string to ASCII bytes. + return "'" . pack( 'H*', substr( $default_value, 2, -1 ) ) . "'"; + } + + // Unquoted string literal. E.g.: abc + return $this->escape_mysql_string_literal( $default_value ); + } + + /** + * Get a MySQL column or index data type from legacy data types cache table. + * + * This method retrieves MySQL column or index data types from a special table + * that was used by an old version of the SQLite driver and that is otherwise + * no longer needed. This is more precise than direct inference from SQLite. + * + * For columns, it returns full column type, including prefix length, e.g.: + * int(11), bigint(20) unsigned, varchar(255), longtext + * + * For indexes, it returns one of: + * KEY, PRIMARY, UNIQUE, FULLTEXT, SPATIAL + * + * @param string $table_name The table name. + * @param string $column_or_index_name The column or index name. + * @return string|null The MySQL definition, or null when not found. + */ + private function get_cached_mysql_data_type( string $table_name, string $column_or_index_name ): ?string { + try { + $mysql_type = $this->driver->execute_sqlite_query( + 'SELECT mysql_type FROM _mysql_data_types_cache WHERE `table` = ? AND column_or_index = ?', + array( $table_name, $column_or_index_name ) + )->fetchColumn(); + } catch ( PDOException $e ) { + if ( str_contains( $e->getMessage(), 'no such table' ) ) { + return null; + } + throw $e; + } + + // Normalize index type for backward compatibility. Some older versions + // of the SQLite driver stored index types with a " KEY" suffix, e.g., + // "PRIMARY KEY" or "UNIQUE KEY". More recent versions omit the suffix. + if ( str_ends_with( $mysql_type, ' KEY' ) ) { + $mysql_type = substr( $mysql_type, 0, strlen( $mysql_type ) - strlen( ' KEY' ) ); + } + return $mysql_type; + } + + /** + * Get a MySQL column type from an SQLite column type. + * + * This method converts an SQLite column type to a MySQL column type as per + * the SQLite column type affinity rules: + * https://sqlite.org/datatype3.html#determination_of_column_affinity + * + * @param string $column_type The SQLite column type. + * @return string The MySQL column type. + */ + private function get_mysql_column_type( string $column_type ): string { + $type = strtoupper( $column_type ); + + /* + * Following the rules of column affinity: + * https://sqlite.org/datatype3.html#determination_of_column_affinity + */ + + // 1. If the declared type contains the string "INT" then it is assigned + // INTEGER affinity. + if ( str_contains( $type, 'INT' ) ) { + return 'int'; + } + + // 2. If the declared type of the column contains any of the strings + // "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity. + if ( str_contains( $type, 'TEXT' ) || str_contains( $type, 'CHAR' ) || str_contains( $type, 'CLOB' ) ) { + return 'text'; + } + + // 3. If the declared type for a column contains the string "BLOB" or + // if no type is specified then the column has affinity BLOB. + if ( str_contains( $type, 'BLOB' ) || '' === $type ) { + return 'blob'; + } + + // 4. If the declared type for a column contains any of the strings + // "REAL", "FLOA", or "DOUB" then the column has REAL affinity. + if ( str_contains( $type, 'REAL' ) || str_contains( $type, 'FLOA' ) ) { + return 'float'; + } + if ( str_contains( $type, 'DOUB' ) ) { + return 'double'; + } + + /** + * 5. Otherwise, the affinity is NUMERIC. + * + * While SQLite defaults to a NUMERIC column affinity, it's better to use + * TEXT in this case, because numeric SQLite columns in non-strict tables + * can contain any text data as well, when it is not a well-formed number. + * + * See: https://sqlite.org/datatype3.html#type_affinity + */ + return 'text'; + } + + /** + * Escape a string literal for MySQL DEFAULT values. + * + * @param string $literal The string literal to escape. + * @return string The escaped string literal. + */ + private function escape_mysql_string_literal( string $literal ): string { + // See: https://www.php.net/manual/en/mysqli.real-escape-string.php + return "'" . addcslashes( $literal, "\0\n\r'\"\Z" ) . "'"; + } + + /** + * Unquote a quoted MySQL identifier. + * + * Remove bounding quotes and replace escaped quotes with their values. + * + * @param string $quoted_identifier The quoted identifier value. + * @return string The unquoted identifier value. + */ + private function unquote_mysql_identifier( string $quoted_identifier ): string { + $first_byte = $quoted_identifier[0] ?? null; + if ( '"' === $first_byte || '`' === $first_byte ) { + $unquoted = substr( $quoted_identifier, 1, -1 ); + return str_replace( $first_byte . $first_byte, $first_byte, $unquoted ); + } + return $quoted_identifier; + } +} diff --git a/wp-includes/sqlite/class-wp-sqlite-db.php b/wp-includes/sqlite/class-wp-sqlite-db.php index 914e430..418c349 100644 --- a/wp-includes/sqlite/class-wp-sqlite-db.php +++ b/wp-includes/sqlite/class-wp-sqlite-db.php @@ -38,6 +38,20 @@ class WP_SQLite_DB extends wpdb { * @param string $dbname Database name. */ public function __construct( $dbname ) { + /** + * We need to initialize the "$wpdb" global early, so that the SQLite + * driver can configure the database. The call stack goes like this: + * + * 1. The "parent::__construct()" call executes "$this->db_connect()". + * 2. The database connection call initializes the SQLite driver. + * 3. The SQLite driver initializes and runs "WP_SQLite_Configurator". + * 4. The configurator uses "WP_SQLite_Information_Schema_Reconstructor", + * which requires "wp-admin/includes/schema.php" when in WordPress. + * 5. The "wp-admin/includes/schema.php" requires the "$wpdb" global, + * which creates a circular dependency. + */ + $GLOBALS['wpdb'] = $this; + parent::__construct( '', '', $dbname, '' ); $this->charset = 'utf8mb4'; } @@ -300,32 +314,36 @@ public function db_connect( $allow_bail = true ) { require_once __DIR__ . '/../../wp-includes/mysql/class-wp-mysql-token.php'; require_once __DIR__ . '/../../wp-includes/mysql/class-wp-mysql-lexer.php'; require_once __DIR__ . '/../../wp-includes/mysql/class-wp-mysql-parser.php'; + require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-connection.php'; + require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-configurator.php'; require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-driver.php'; require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-driver-exception.php'; require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php'; + require_once __DIR__ . '/../../wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php'; $this->ensure_database_directory( FQDB ); try { - $this->dbh = new WP_SQLite_Driver( + $connection = new WP_SQLite_Connection( array( - 'connection' => $pdo, - 'path' => FQDB, - 'database' => $this->dbname, - 'sqlite_journal_mode' => defined( 'SQLITE_JOURNAL_MODE' ) ? SQLITE_JOURNAL_MODE : null, + 'pdo' => $pdo, + 'path' => FQDB, + 'journal_mode' => defined( 'SQLITE_JOURNAL_MODE' ) ? SQLITE_JOURNAL_MODE : null, ) ); + $this->dbh = new WP_SQLite_Driver( $connection, $this->dbname ); + $GLOBALS['@pdo'] = $this->dbh->get_connection()->get_pdo(); } catch ( Throwable $e ) { $this->last_error = $this->format_error_message( $e ); } } else { $this->dbh = new WP_SQLite_Translator( $pdo ); $this->last_error = $this->dbh->get_error_message(); + $GLOBALS['@pdo'] = $this->dbh->get_pdo(); } if ( $this->last_error ) { return false; } - $GLOBALS['@pdo'] = $this->dbh->get_pdo(); - $this->ready = true; + $this->ready = true; $this->set_sql_mode(); } diff --git a/wp-includes/sqlite/db.php b/wp-includes/sqlite/db.php index 9f8ca0a..bb00c33 100644 --- a/wp-includes/sqlite/db.php +++ b/wp-includes/sqlite/db.php @@ -6,6 +6,11 @@ * @since 1.0.0 */ +/** + * Load the "SQLITE_DRIVER_VERSION" constant. + */ +require_once dirname( __DIR__, 2 ) . '/version.php'; + // Require the constants file. require_once dirname( __DIR__, 2 ) . '/constants.php'; diff --git a/wp-includes/sqlite/install-functions.php b/wp-includes/sqlite/install-functions.php index bdfb006..022afc8 100644 --- a/wp-includes/sqlite/install-functions.php +++ b/wp-includes/sqlite/install-functions.php @@ -35,10 +35,8 @@ function sqlite_make_db_sqlite() { if ( defined( 'WP_SQLITE_AST_DRIVER' ) && WP_SQLITE_AST_DRIVER ) { $translator = new WP_SQLite_Driver( - array( - 'database' => $wpdb->dbname, - 'connection' => $pdo, - ) + new WP_SQLite_Connection( array( 'pdo' => $pdo ) ), + $wpdb->dbname ); } else { $translator = new WP_SQLite_Translator( $pdo );