From 8aefd240a8c3d5d8b69df84f22a81c1535f56427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Jake=C5=A1?= Date: Tue, 21 Apr 2026 13:34:52 +0200 Subject: [PATCH 1/2] Report AUTO_INCREMENT values in metadata commands Compute the MySQL Auto_increment value live from SQLite's sqlite_sequence so SHOW TABLE STATUS, SHOW CREATE TABLE, and queries against INFORMATION_SCHEMA.TABLES report the next AUTO_INCREMENT value instead of always returning NULL. The counter is preserved past DELETE and reset by TRUNCATE, matching MySQL InnoDB semantics. Retires the first half of patchWp15AdminPostAutoIncrement in WordPress, which existed to work around WP 1.5's wp-admin/post.php reading the SHOW TABLE STATUS Auto_increment column. --- .../sqlite/class-wp-pdo-mysql-on-sqlite.php | 143 +++++++++++++----- .../tests/WP_SQLite_Driver_Metadata_Tests.php | 121 +++++++++++++++ .../tests/WP_SQLite_Driver_Tests.php | 4 +- 3 files changed, 231 insertions(+), 37 deletions(-) diff --git a/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php b/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php index 34171ac7f..3a212eb06 100644 --- a/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php +++ b/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php @@ -2958,41 +2958,64 @@ private function execute_show_table_status_statement( WP_Parser_Node $node ): vo // LIKE and WHERE clauses. $like_or_where = $node->get_first_child_node( 'likeOrWhere' ); if ( null !== $like_or_where ) { - $condition = $this->translate_show_like_or_where_condition( $like_or_where, 'table_name' ); + $condition = $this->translate_show_like_or_where_condition( $like_or_where, 'Name' ); } - // Fetch table information. - $tables_tables = $this->information_schema_builder->get_table_name( - false, // SHOW TABLE STATUS lists only non-temporary tables. - 'tables' + // SHOW TABLE STATUS lists only non-temporary tables. + $tables_table = $this->information_schema_builder->get_table_name( false, 'tables' ); + $columns_table = $this->information_schema_builder->get_table_name( false, 'columns' ); + + // Compose a subquery to compute auto-increment values. + $has_sequence_table = (bool) $this->execute_sqlite_query( + "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'" + )->fetchColumn(); + + $auto_increment_subquery = sprintf( + "( + SELECT COALESCE(s.seq + 1, 1) + FROM %s AS c + %s + WHERE c.extra = 'auto_increment' + AND c.table_schema = t.table_schema + AND c.table_name = t.table_name + )", + $this->quote_sqlite_identifier( $columns_table ), + $has_sequence_table + ? 'LEFT JOIN main.sqlite_sequence AS s ON s.name = c.table_name' + : 'LEFT JOIN (SELECT 0 AS seq) AS s' ); - $query = sprintf( - 'SELECT - table_name AS `Name`, - engine AS `Engine`, - version AS `Version`, - row_format AS `Row_format`, - table_rows AS `Rows`, - avg_row_length AS `Avg_row_length`, - data_length AS `Data_length`, - max_data_length AS `Max_data_length`, - index_length AS `Index_length`, - data_free AS `Data_free`, - auto_increment AS `Auto_increment`, - create_time AS `Create_time`, - update_time AS `Update_time`, - check_time AS `Check_time`, - table_collation AS `Collation`, - checksum AS `Checksum`, - create_options AS `Create_options`, - table_comment AS `Comment` - FROM %s - WHERE table_schema = ? %s - ORDER BY table_name', - $this->quote_sqlite_identifier( $tables_tables ), + + $query = sprintf( + 'SELECT * FROM ( + SELECT + table_name AS `Name`, + engine AS `Engine`, + version AS `Version`, + row_format AS `Row_format`, + table_rows AS `Rows`, + avg_row_length AS `Avg_row_length`, + data_length AS `Data_length`, + max_data_length AS `Max_data_length`, + index_length AS `Index_length`, + data_free AS `Data_free`, + %s AS `Auto_increment`, + create_time AS `Create_time`, + update_time AS `Update_time`, + check_time AS `Check_time`, + table_collation AS `Collation`, + checksum AS `Checksum`, + create_options AS `Create_options`, + table_comment AS `Comment` + FROM %s AS t + WHERE table_schema = ? + ) + WHERE 1 %s + ORDER BY `Name`', + $auto_increment_subquery, + $this->quote_sqlite_identifier( $tables_table ), $condition ?? '' ); - $params = array( + $params = array( $this->get_saved_db_name( $database ), ); @@ -4697,7 +4720,7 @@ public function translate_table_ref( WP_Parser_Node $node ): string { $table_name = $this->unquote_sqlite_identifier( $this->translate( $table ) ); // When the table reference targets an information schema table, - // we need to inject the configured database name dynamically. + // we need to inject some additional values dynamically. if ( ( null === $schema_name && 'information_schema' === $this->db_name ) || ( null !== $schema_name && 'information_schema' === strtolower( $schema_name ) ) @@ -4743,14 +4766,40 @@ public function translate_table_ref( WP_Parser_Node $node ): string { $expanded_list = array(); foreach ( $columns as $column ) { $quoted_column = $this->quote_sqlite_identifier( $column ); - if ( isset( $information_schema_db_column_map[ strtoupper( $column ) ] ) ) { + if ( isset( $information_schema_db_column_map[ $column ] ) ) { + // Replace the database name with the configured database name. $expanded_list[] = sprintf( "CASE WHEN %s = 'information_schema' THEN %s ELSE %s END AS %s", $quoted_column, $quoted_column, $this->quote_sqlite_value( $this->main_db_name ), - strtoupper( $quoted_column ) + $quoted_column + ); + } elseif ( 'tables' === $table_name && 'AUTO_INCREMENT' === $column ) { + // Inject the auto-increment values. + $columns_table = $this->information_schema_builder->get_table_name( false, 'columns' ); + $has_sequence_table = (bool) $this->execute_sqlite_query( + "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'" + )->fetchColumn(); + + $auto_increment_subquery = sprintf( + "( + SELECT COALESCE(s.seq + 1, 1) + FROM %s AS c + %s + WHERE c.extra = 'auto_increment' + AND c.table_schema = %s.table_schema + AND c.table_name = %s.table_name + )", + $this->quote_sqlite_identifier( $columns_table ), + $has_sequence_table + ? 'LEFT JOIN main.sqlite_sequence AS s ON s.name = c.table_name' + : 'LEFT JOIN (SELECT 0 AS seq) AS s', + $this->quote_sqlite_identifier( $table_name ), + $this->quote_sqlite_identifier( $table_name ) ); + + $expanded_list[] = sprintf( '%s AS %s', $auto_increment_subquery, $quoted_column ); } else { $expanded_list[] = $quoted_column; } @@ -6303,7 +6352,8 @@ private function get_mysql_create_table_statement( bool $table_is_temporary, str )->fetchAll( PDO::FETCH_ASSOC ); // 6. Generate CREATE TABLE statement columns. - $rows = array(); + $rows = array(); + $has_auto_increment = false; foreach ( $column_info as $column ) { $sql = ' '; $sql .= $this->quote_mysql_identifier( $column['COLUMN_NAME'] ); @@ -6315,7 +6365,8 @@ private function get_mysql_create_table_statement( bool $table_is_temporary, str $sql .= ' NULL'; } if ( 'auto_increment' === $column['EXTRA'] ) { - $sql .= ' AUTO_INCREMENT'; + $has_auto_increment = true; + $sql .= ' AUTO_INCREMENT'; } // Handle DEFAULT CURRENT_TIMESTAMP. This works only with timestamp @@ -6454,6 +6505,28 @@ function ( $column ) { $sql .= implode( ",\n", $rows ); $sql .= "\n)"; $sql .= sprintf( ' ENGINE=%s', $table_info['ENGINE'] ); + + // Add "AUTO_INCREMENT=N" if a sequence exists and has been advanced. + if ( $has_auto_increment ) { + try { + $seq = (int) $this->execute_sqlite_query( + sprintf( + 'SELECT seq FROM %s.sqlite_sequence WHERE name = ?', + $table_is_temporary ? 'temp' : 'main' + ), + array( $table_name ) + )->fetchColumn(); + } catch ( PDOException $e ) { + if ( ! str_contains( $e->getMessage(), 'no such table' ) ) { + throw $e; + } + $seq = 0; + } + if ( $seq > 0 ) { + $sql .= sprintf( ' AUTO_INCREMENT=%d', $seq + 1 ); + } + } + $sql .= sprintf( ' DEFAULT CHARSET=%s', $charset ); $sql .= sprintf( ' COLLATE=%s', $collation ); if ( '' !== $table_info['TABLE_COMMENT'] ) { diff --git a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php index 05770e307..4668caaec 100644 --- a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php +++ b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php @@ -628,6 +628,127 @@ public function testShowTableStatus() { ); } + public function testInformationSchemaTablesAutoIncrement(): void { + // A non-AUTO_INCREMENT table reports NULL. + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT)' ); + $result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE table_name = 'plain'" ); + $this->assertSame( null, $result[0]->AUTO_INCREMENT ); + + // A fresh AUTO_INCREMENT table reports 1. + $this->assertQuery( 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE table_name = 't'" ); + $this->assertSame( '1', $result[0]->AUTO_INCREMENT ); + + // After inserts, the sequence advances. + $this->assertQuery( "INSERT INTO t (name) VALUES ('a'), ('b'), ('c')" ); + $result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE table_name = 't'" ); + $this->assertSame( '4', $result[0]->AUTO_INCREMENT ); + + // DELETE preserves the counter. + $this->assertQuery( 'DELETE FROM t' ); + $result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE table_name = 't'" ); + $this->assertSame( '4', $result[0]->AUTO_INCREMENT ); + + // TRUNCATE resets the counter. + $this->assertQuery( 'TRUNCATE TABLE t' ); + $result = $this->assertQuery( "SELECT * FROM information_schema.tables WHERE table_name = 't'" ); + $this->assertSame( '1', $result[0]->AUTO_INCREMENT ); + } + + public function testInformationSchemaTablesFilterByAutoIncrement(): void { + $this->assertQuery( 'CREATE TABLE low (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $this->assertQuery( 'CREATE TABLE high (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT)' ); + $this->assertQuery( "INSERT INTO low (name) VALUES ('a')" ); + $this->assertQuery( "INSERT INTO high (name) VALUES ('a'), ('b'), ('c'), ('d'), ('e')" ); + + // > 3 + $result = $this->assertQuery( + 'SELECT TABLE_NAME FROM information_schema.tables WHERE `AUTO_INCREMENT` > 3' + ); + $this->assertCount( 1, $result ); + $this->assertSame( 'high', $result[0]->TABLE_NAME ); + + // IS NULL + $result = $this->assertQuery( + 'SELECT TABLE_NAME FROM information_schema.tables WHERE `AUTO_INCREMENT` IS NULL' + ); + $this->assertCount( 1, $result ); + $this->assertSame( 'plain', $result[0]->TABLE_NAME ); + } + + public function testShowTableStatusAutoIncrement(): void { + // A non-AUTO_INCREMENT table reports NULL. + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT)' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 'plain'" ); + $this->assertSame( null, $result[0]->Auto_increment ); + + // A fresh AUTO_INCREMENT table reports 1. + $this->assertQuery( 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '1', $result[0]->Auto_increment ); + + // After inserts, the sequence advances. + $this->assertQuery( "INSERT INTO t (name) VALUES ('a'), ('b'), ('c')" ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '4', $result[0]->Auto_increment ); + + // DELETE preserves the counter. + $this->assertQuery( 'DELETE FROM t' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '4', $result[0]->Auto_increment ); + + // TRUNCATE resets the counter. + $this->assertQuery( 'TRUNCATE TABLE t' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '1', $result[0]->Auto_increment ); + } + + public function testShowTableStatusFilterByAutoIncrement(): void { + $this->assertQuery( 'CREATE TABLE low (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $this->assertQuery( 'CREATE TABLE high (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT)' ); + $this->assertQuery( "INSERT INTO low (name) VALUES ('a')" ); + $this->assertQuery( "INSERT INTO high (name) VALUES ('a'), ('b'), ('c'), ('d'), ('e')" ); + + // > 3 + $result = $this->assertQuery( 'SHOW TABLE STATUS WHERE `Auto_increment` > 3' ); + $this->assertCount( 1, $result ); + $this->assertSame( 'high', $result[0]->Name ); + + // IS NULL + $result = $this->assertQuery( 'SHOW TABLE STATUS WHERE `Auto_increment` IS NULL' ); + $this->assertCount( 1, $result ); + $this->assertSame( 'plain', $result[0]->Name ); + } + + public function testShowCreateTableAutoIncrement(): void { + // No AUTO_INCREMENT=N on a non-AUTO_INCREMENT table. + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT)' ); + $result = $this->assertQuery( 'SHOW CREATE TABLE plain' ); + $this->assertStringNotContainsString( 'AUTO_INCREMENT=', $result[0]->{'Create Table'} ); + + // No AUTO_INCREMENT=N on a fresh AUTO_INCREMENT table (sequence not advanced). + $this->assertQuery( 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringNotContainsString( 'AUTO_INCREMENT=', $result[0]->{'Create Table'} ); + + // After inserts, the sequence advances. + $this->assertQuery( "INSERT INTO t (name) VALUES ('a'), ('b'), ('c')" ); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringContainsString( 'AUTO_INCREMENT=4', $result[0]->{'Create Table'} ); + + // DELETE preserves the counter. + $this->assertQuery( 'DELETE FROM t' ); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringContainsString( 'AUTO_INCREMENT=4', $result[0]->{'Create Table'} ); + + // TRUNCATE resets the counter. + $this->assertQuery( 'TRUNCATE TABLE t' ); + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringNotContainsString( 'AUTO_INCREMENT=', $result[0]->{'Create Table'} ); + } + public function testShowFullColumns(): void { $this->assertQuery( "CREATE TABLE t (id INT COMMENT 'Comment ID', name TEXT COMMENT 'Comment Name')" ); $result = $this->assertQuery( 'SHOW FULL COLUMNS FROM t' ); diff --git a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Tests.php b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Tests.php index 4daf7ca7f..1695b953f 100644 --- a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Tests.php +++ b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Tests.php @@ -889,7 +889,7 @@ public function testShowTableStatusWhere() { ); $this->assertQuery( - "SHOW TABLE STATUS WHERE SUBSTR(table_name, 11, 1) = '1'" + "SHOW TABLE STATUS WHERE SUBSTR(Name, 11, 1) = '1'" ); $this->assertCount( 1, @@ -4562,7 +4562,7 @@ public function testCompoundPrimaryKeyWithAutoincrement(): void { ' `id` int NOT NULL AUTO_INCREMENT,', ' `name` varchar(32) NOT NULL,', ' PRIMARY KEY (`id`, `name`)', - ') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci', + ') ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci', ) ), $results[0]->{'Create Table'} From 7e28af9ab0476e6f229ea37cbaa7a26ad5499b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Jake=C5=A1?= Date: Tue, 21 Apr 2026 13:35:05 +0200 Subject: [PATCH 2/2] Support setting AUTO_INCREMENT via CREATE TABLE / ALTER TABLE Implement the AUTO_INCREMENT table option in CREATE TABLE and ALTER TABLE by writing the requested value as seq - 1 into SQLite's sqlite_sequence. As in MySQL, the effective counter is clamped to MAX(col) so it never drops below an existing row, and the option is silently ignored when the table has no AUTO_INCREMENT column. Also skip the full table rebuild in ALTER TABLE when the statement only carries table options (no alterListItem) so that pure AUTO_INCREMENT = N changes are instant rather than a no-op full-table copy. --- .../sqlite/class-wp-pdo-mysql-on-sqlite.php | 94 +++++++++++++++- .../tests/WP_SQLite_Driver_Metadata_Tests.php | 100 ++++++++++++++++++ 2 files changed, 192 insertions(+), 2 deletions(-) diff --git a/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php b/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php index 3a212eb06..cbd876cf4 100644 --- a/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php +++ b/packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php @@ -2378,6 +2378,9 @@ private function execute_create_table_statement( WP_Parser_Node $node ): void { foreach ( $constraint_queries as $query ) { $this->execute_sqlite_query( $query ); } + + // Apply AUTO_INCREMENT = N table option, if any. + $this->apply_auto_increment_table_option( $table_is_temporary, $table_name, $node ); } /** @@ -2451,8 +2454,18 @@ private function execute_alter_table_statement( WP_Parser_Node $node ): void { } } - $this->information_schema_builder->record_alter_table( $node ); - $this->recreate_table_from_information_schema( $table_is_temporary, $table_name, $column_map ); + /* + * Skip the expensive table rebuild when the statement only carries + * table options (e.g. ALTER TABLE t AUTO_INCREMENT = N). These don't + * change the schema, so the recreate would be a pointless full copy. + */ + if ( count( $node->get_descendant_nodes( 'alterListItem' ) ) > 0 ) { + $this->information_schema_builder->record_alter_table( $node ); + $this->recreate_table_from_information_schema( $table_is_temporary, $table_name, $column_map ); + } + + // Apply AUTO_INCREMENT = N table option, if any. + $this->apply_auto_increment_table_option( $table_is_temporary, $table_name, $node ); // @TODO: Consider using a "fast path" for ALTER TABLE statements that // consist only of operations that SQLite's ALTER TABLE supports. @@ -4916,6 +4929,83 @@ private function recreate_table_from_information_schema( // @TODO: Triggers and views. } + /** + * Apply the AUTO_INCREMENT table option from a CREATE TABLE or ALTER TABLE + * statement by adjusting the row in SQLite's "sqlite_sequence" table. + * + * @param bool $table_is_temporary Whether the table is temporary. + * @param string $table_name The table name. + * @param WP_Parser_Node $node The "createStatement" or "alterStatement" AST node. + */ + private function apply_auto_increment_table_option( + bool $table_is_temporary, + string $table_name, + WP_Parser_Node $node + ): void { + // Find the last AUTO_INCREMENT = N option (MySQL uses the last one). + $value = null; + foreach ( $node->get_descendant_nodes( 'createTableOption' ) as $option ) { + if ( ! $option->has_child_token( WP_MySQL_Lexer::AUTO_INCREMENT_SYMBOL ) ) { + continue; + } + $number_node = $option->get_first_child_node( 'ulonglong_number' ); + if ( null === $number_node ) { + continue; + } + $value = (int) $number_node->get_first_descendant_token()->get_value(); + } + if ( null === $value ) { + return; + } + + // Find the AUTO_INCREMENT column. + $columns_table = $this->information_schema_builder->get_table_name( $table_is_temporary, 'columns' ); + $auto_column = $this->execute_sqlite_query( + sprintf( + "SELECT column_name FROM %s + WHERE table_schema = ? + AND table_name = ? + AND extra = 'auto_increment'", + $this->quote_sqlite_identifier( $columns_table ) + ), + array( $this->get_saved_db_name(), $table_name ) + )->fetchColumn(); + if ( false === $auto_column ) { + return; + } + + /* + * Prepare an expression for the sequence value. + * 1. Use N - 1. MySQL stores the next value, SQLite the last one. + * 2. Clamp to MAX(col) like MySQL (we can't go below existing values). + * + * The value is inlined as an integer literal because PDO binds PHP ints + * as TEXT, and SQLite's type affinity ranks TEXT above INTEGER in MAX(). + */ + $schema = $table_is_temporary ? 'temp' : 'main'; + $seq_expr = sprintf( + 'MAX(%d, COALESCE((SELECT MAX(%s) FROM %s), 0))', + $value - 1, + $this->quote_sqlite_identifier( $auto_column ), + $this->quote_sqlite_identifier( $table_name ) + ); + + // Update the value in the "sqlite_sequence" table. + $updated = $this->execute_sqlite_query( + sprintf( 'UPDATE %s.sqlite_sequence SET seq = %s WHERE name = ?', $schema, $seq_expr ), + array( $table_name ) + )->rowCount(); + + // If the sequence value does not exist yet, insert a new row. + // SQLite reports matched (not affected) rows, so the 0 check is safe. + if ( 0 === $updated ) { + $this->execute_sqlite_query( + sprintf( 'INSERT INTO %s.sqlite_sequence (name, seq) VALUES (?, %s)', $schema, $seq_expr ), + array( $table_name ) + ); + } + } + /** * Translate a MySQL SHOW LIKE ... or SHOW WHERE ... condition to SQLite. * diff --git a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php index 4668caaec..6b6f4b7fe 100644 --- a/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php +++ b/packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Metadata_Tests.php @@ -749,6 +749,106 @@ public function testShowCreateTableAutoIncrement(): void { $this->assertStringNotContainsString( 'AUTO_INCREMENT=', $result[0]->{'Create Table'} ); } + + public function testCreateTableSetAutoIncrement(): void { + $this->assertQuery( + 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT) AUTO_INCREMENT=100' + ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '100', $result[0]->Auto_increment ); + + // INSERT advances the sequence. + $this->assertQuery( "INSERT INTO t (name) VALUES ('a')" ); + $this->assertSame( '100', $this->assertQuery( 'SELECT id FROM t' )[0]->id ); + + // SHOW TABLE STATUS + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '101', $result[0]->Auto_increment ); + + // SHOW CREATE TABLE + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringContainsString( 'AUTO_INCREMENT=101', $result[0]->{'Create Table'} ); + + // Without an AUTO_INCREMENT column, the option is ignored. + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT) AUTO_INCREMENT=500' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 'plain'" ); + $this->assertSame( null, $result[0]->Auto_increment ); + } + + public function testAlterTableSetAutoIncrement(): void { + $this->assertQuery( 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + + // Set the sequence value on an empty table. + $this->assertQuery( 'ALTER TABLE t AUTO_INCREMENT = 50' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '50', $result[0]->Auto_increment ); + + // INSERT advances the sequence. + $this->assertQuery( "INSERT INTO t (name) VALUES ('a')" ); + $this->assertSame( '50', $this->assertQuery( 'SELECT id FROM t' )[0]->id ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '51', $result[0]->Auto_increment ); + + // Update the sequence value. + $this->assertQuery( 'ALTER TABLE t AUTO_INCREMENT = 200' ); + + // SHOW TABLE STATUS + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '200', $result[0]->Auto_increment ); + + // SHOW CREATE TABLE + $result = $this->assertQuery( 'SHOW CREATE TABLE t' ); + $this->assertStringContainsString( 'AUTO_INCREMENT=200', $result[0]->{'Create Table'} ); + + // Lowering the counter at or below MAX(id) clamps to MAX(id) + 1. + $this->assertQuery( 'ALTER TABLE t AUTO_INCREMENT = 1' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '51', $result[0]->Auto_increment ); + + // Without an AUTO_INCREMENT column, the option is ignored. + $this->assertQuery( 'CREATE TABLE plain (id INT, name TEXT) AUTO_INCREMENT=500' ); + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 'plain'" ); + $this->assertSame( null, $result[0]->Auto_increment ); + } + + public function testTemporaryTableAutoIncrement(): void { + // Persistent and temporary tables must keep independent sequences. + + // Persistent table (next sequence value is 3). + $this->assertQuery( 'CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)' ); + $this->assertQuery( "INSERT INTO t (name) VALUES ('a'), ('b')" ); + + // Temporary table with the same name (seeded to start at 500). + $this->assertQuery( + 'CREATE TEMPORARY TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT) AUTO_INCREMENT=500' + ); + + // SHOW TABLE STATUS lists only persistent tables. + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertCount( 1, $result ); + $this->assertSame( '3', $result[0]->Auto_increment ); + + // INFORMATION_SCHEMA.TABLES lists only persistent tables. + $result = $this->assertQuery( + "SELECT `AUTO_INCREMENT` FROM information_schema.tables WHERE table_name = 't'" + ); + $this->assertCount( 1, $result ); + $this->assertSame( '3', $result[0]->AUTO_INCREMENT ); + + // Unqualified statements target the temporary table. + $this->assertQuery( "INSERT INTO t (name) VALUES ('x')" ); + $result = $this->assertQuery( "SELECT id FROM t WHERE name = 'x'" ); + $this->assertSame( '500', $result[0]->id ); + + $this->assertQuery( 'ALTER TABLE t AUTO_INCREMENT = 1000' ); + $this->assertQuery( "INSERT INTO t (name) VALUES ('y')" ); + $this->assertSame( '1000', $this->assertQuery( "SELECT id FROM t WHERE name = 'y'" )[0]->id ); + + // The persistent table's sequence remains unaffected. + $result = $this->assertQuery( "SHOW TABLE STATUS LIKE 't'" ); + $this->assertSame( '3', $result[0]->Auto_increment ); + } + public function testShowFullColumns(): void { $this->assertQuery( "CREATE TABLE t (id INT COMMENT 'Comment ID', name TEXT COMMENT 'Comment Name')" ); $result = $this->assertQuery( 'SHOW FULL COLUMNS FROM t' );