From 40bdc781bf9190ccaac8e2ebd24b29927ab9222d Mon Sep 17 00:00:00 2001 From: Jeroen P Date: Fri, 26 Jul 2024 21:50:31 +0700 Subject: [PATCH] Allow double underscores in index names (#136) See: https://github.com/WordPress/sqlite-database-integration/pull/133#discussion_r1690678568 When creating new tables in SQLite, original index names are prefixed with the table name. This is because unlike MySQL, SQLite does not allow identical index names across the database. Adding a table prefix ensures the index names are unique. When running a `show create table` query, we restore the original index name by removing the table prefix. We split on double underscores but we did not take into account that the original index name can also contain `__`. This PR fixes an issue where we explode without a limit. We only want to explode on the first occurrence of `__`. Unit tests have been added to ensure this behavior. --- tests/WP_SQLite_Translator_Tests.php | 34 +++++++++++++++++-- .../sqlite/class-wp-sqlite-translator.php | 7 ++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/tests/WP_SQLite_Translator_Tests.php b/tests/WP_SQLite_Translator_Tests.php index 82ed6ec..4118f5e 100644 --- a/tests/WP_SQLite_Translator_Tests.php +++ b/tests/WP_SQLite_Translator_Tests.php @@ -377,7 +377,8 @@ public function testCreateTablseWithIdenticalIndexNames() { ID BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, option_name VARCHAR(255) default '', option_value TEXT NOT NULL, - KEY `option_name` (`option_name`) + KEY `option_name` (`option_name`), + KEY `double__underscores` (`option_name`, `ID`) );" ); @@ -386,11 +387,40 @@ public function testCreateTablseWithIdenticalIndexNames() { ID BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, option_name VARCHAR(255) default '', option_value TEXT NOT NULL, - KEY `option_name` (`option_name`) + KEY `option_name` (`option_name`), + KEY `double__underscores` (`option_name`, `ID`) );" ); } + public function testShowCreateTablePreservesDoubleUnderscoreKeyNames() { + $this->assertQuery( + "CREATE TABLE _tmp__table ( + ID BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, + option_name VARCHAR(255) default '', + option_value TEXT NOT NULL, + KEY `option_name` (`option_name`), + KEY `double__underscores` (`option_name`, `ID`) + );" + ); + + $this->assertQuery( + 'SHOW CREATE TABLE _tmp__table;' + ); + $results = $this->engine->get_query_results(); + $this->assertEquals( + 'CREATE TABLE `_tmp__table` ( + `ID` bigint NOT NULL AUTO_INCREMENT, + `option_name` varchar(255) DEFAULT \'\', + `option_value` text NOT NULL DEFAULT \'\', + PRIMARY KEY (`ID`), + KEY `double__underscores` (`option_name`, `ID`), + KEY `option_name` (`option_name`) +);', + $results[0]->{'Create Table'} + ); + } + public function testShowCreateTableWithPrimaryKeyColumnsReverseOrdered() { $this->assertQuery( 'CREATE TABLE `_tmp_table` ( diff --git a/wp-includes/sqlite/class-wp-sqlite-translator.php b/wp-includes/sqlite/class-wp-sqlite-translator.php index 40a4fc0..2730d85 100644 --- a/wp-includes/sqlite/class-wp-sqlite-translator.php +++ b/wp-includes/sqlite/class-wp-sqlite-translator.php @@ -3570,9 +3570,7 @@ private function get_key_definitions( $table_name, $columns ) { $key_definition[] = 'KEY'; // Remove the prefix from the index name if there is any. We use __ as a separator. - $index_name = strstr( $key['index']['name'], '__' ) - ? explode( '__', $key['index']['name'] )[1] - : $key['index']['name']; + $index_name = explode( '__', $key['index']['name'], 2 )[1] ?? $key['index']['name']; $key_definition[] = sprintf( '`%s`', $index_name ); @@ -4214,7 +4212,8 @@ public function rollback() { * @return string */ private function generate_index_name( $table, $original_index_name ) { - // Strip the occurrences of 2 or more consecutive underscores to allow easier splitting on __ later. + // Strip the occurrences of 2 or more consecutive underscores from the table name + // to allow easier splitting on __ later. return preg_replace( '/_{2,}/', '_', $table ) . '__' . $original_index_name; } }