Skip to content

Commit 9d35fe4

Browse files
authored
Support fully qualified table names in all statement types (#270)
This PR: 1. Adds support for fully qualified table names (like `wp.my_table`) for all statement types (they weren't supported before): ```sql CREATE TABLE wp.t ...; DROP TABLE wp.t; INSERT INTO wp.t (id) ...; SELECT * FROM wp.t; UPDATE wp.t SET ...; DELETE FROM wp.t; TRUNCATE TABLE wp.t; SHOW CREATE TABLE wp.t; SHOW COLUMNS FROM wp.t; SHOW COLUMNS FROM t FROM wp; SHOW INDEXES FROM wp.t; SHOW INDEXES FROM t FROM wp; DESCRIBE wp.t; SHOW COLUMNS FROM wp.t; SHOW INDEXES FROM wp.t; SHOW INDEXES FROM wp.t; LOCK TABLES wp.t READ; ANALYZE TABLE wp.t; CHECK TABLE wp.t; OPTIMIZE TABLE wp.t; REPAIR TABLE wp.t; ``` 2. Enables read usage of information schema tables in write queries: ```sql INSERT INTO t (id, value) SELECT 1, table_name FROM information_schema.tables; INSERT INTO t (id, value) SELECT 2, table_name FROM (SELECT table_name FROM information_schema.tables); INSERT INTO t (id, value) SELECT 3, it.table_name FROM information_schema.schemata s JOIN information_schema.tables it ON s.schema_name = it.table_schema; DELETE t FROM t JOIN information_schema.tables it ON t.value = it.table_name; ``` Point 1 is needed for phpMyAdmin support, and point 2 naturally emerges from implementing 1.
1 parent b388ebb commit 9d35fe4

File tree

3 files changed

+510
-107
lines changed

3 files changed

+510
-107
lines changed

tests/WP_SQLite_Driver_Tests.php

Lines changed: 276 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4202,7 +4202,7 @@ public function getReservedPrefixTestData(): array {
42024202
* @dataProvider getInformationSchemaIsReadonlyTestData
42034203
*/
42044204
public function testInformationSchemaIsReadonly( string $query ): void {
4205-
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
4205+
$this->assertQuery( 'CREATE TABLE tables (id INT)' );
42064206
$this->expectException( WP_SQLite_Driver_Exception::class );
42074207
$this->expectExceptionMessage( "Access denied for user 'sqlite'@'%' to database 'information_schema'" );
42084208
$this->assertQuery( $query );
@@ -4211,20 +4211,30 @@ public function testInformationSchemaIsReadonly( string $query ): void {
42114211
public function getInformationSchemaIsReadonlyTestData(): array {
42124212
return array(
42134213
array( 'INSERT INTO information_schema.tables (table_name) VALUES ("t")' ),
4214+
array( 'REPLACE INTO information_schema.tables (table_name) VALUES ("t")' ),
42144215
array( 'UPDATE information_schema.tables SET table_name = "new_t" WHERE table_name = "t"' ),
4216+
array( 'UPDATE information_schema.tables, information_schema.columns SET table_name = "new_t" WHERE table_name = "t"' ),
42154217
array( 'DELETE FROM information_schema.tables WHERE table_name = "t"' ),
4218+
array( 'DELETE it FROM t, information_schema.tables it WHERE table_name = "t"' ),
4219+
array( 'TRUNCATE information_schema.tables' ),
42164220
array( 'CREATE TABLE information_schema.new_table (id INT)' ),
42174221
array( 'ALTER TABLE information_schema.tables ADD COLUMN new_column INT' ),
42184222
array( 'DROP TABLE information_schema.tables' ),
4219-
array( 'TRUNCATE information_schema.tables' ),
4223+
array( 'LOCK TABLES information_schema.tables READ' ),
4224+
array( 'CREATE INDEX idx_name ON information_schema.tables (name)' ),
4225+
array( 'DROP INDEX `PRIMARY` ON information_schema.tables' ),
4226+
array( 'ANALYZE TABLE information_schema.tables' ),
4227+
array( 'CHECK TABLE information_schema.tables' ),
4228+
array( 'OPTIMIZE TABLE information_schema.tables' ),
4229+
array( 'REPAIR TABLE information_schema.tables' ),
42204230
);
42214231
}
42224232

42234233
/**
42244234
* @dataProvider getInformationSchemaIsReadonlyWithUseTestData
42254235
*/
42264236
public function testInformationSchemaIsReadonlyWithUse( string $query ): void {
4227-
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
4237+
$this->assertQuery( 'CREATE TABLE tables (id INT)' );
42284238
$this->expectException( WP_SQLite_Driver_Exception::class );
42294239
$this->expectExceptionMessage( "Access denied for user 'sqlite'@'%' to database 'information_schema'" );
42304240
$this->assertQuery( 'USE information_schema' );
@@ -4234,12 +4244,22 @@ public function testInformationSchemaIsReadonlyWithUse( string $query ): void {
42344244
public function getInformationSchemaIsReadonlyWithUseTestData(): array {
42354245
return array(
42364246
array( 'INSERT INTO tables (table_name) VALUES ("t")' ),
4247+
array( 'REPLACE INTO tables (table_name) VALUES ("t")' ),
42374248
array( 'UPDATE tables SET table_name = "new_t" WHERE table_name = "t"' ),
4249+
array( 'UPDATE tables, columns SET table_name = "new_t" WHERE table_name = "t"' ),
42384250
array( 'DELETE FROM tables WHERE table_name = "t"' ),
4251+
array( 'DELETE it FROM t, tables it WHERE table_name = "t"' ),
4252+
array( 'TRUNCATE tables' ),
42394253
array( 'CREATE TABLE new_table (id INT)' ),
42404254
array( 'ALTER TABLE tables ADD COLUMN new_column INT' ),
42414255
array( 'DROP TABLE tables' ),
4242-
array( 'TRUNCATE tables' ),
4256+
array( 'LOCK TABLES tables READ' ),
4257+
array( 'CREATE INDEX idx_name ON tables (name)' ),
4258+
array( 'DROP INDEX `PRIMARY` ON tables' ),
4259+
array( 'ANALYZE TABLE tables' ),
4260+
array( 'CHECK TABLE tables' ),
4261+
array( 'OPTIMIZE TABLE tables' ),
4262+
array( 'REPAIR TABLE tables' ),
42434263
);
42444264
}
42454265

@@ -9537,4 +9557,256 @@ public function testInsertIntoSetSyntaxInNonStrictMode(): void {
95379557
$result
95389558
);
95399559
}
9560+
9561+
public function testFullyQualifiedTableName(): void {
9562+
// Ensure "information_schema.tables" is empty.
9563+
$this->assertQuery( 'DROP TABLE _options, _dates' );
9564+
$result = $this->assertQuery( 'SELECT * FROM information_schema.tables' );
9565+
$this->assertCount( 0, $result );
9566+
9567+
// Switch to the "information_schema" database.
9568+
$this->assertQuery( 'USE information_schema' );
9569+
9570+
// CREATE TABLE
9571+
$this->assertQuery( 'CREATE TABLE wp.t (id INT PRIMARY KEY)' );
9572+
$result = $this->assertQuery( 'SHOW TABLES FROM wp' );
9573+
$this->assertCount( 1, $result );
9574+
9575+
// INSERT
9576+
$this->assertQuery( 'INSERT INTO wp.t (id) VALUES (1)' );
9577+
$result = $this->assertQuery( 'SELECT * FROM wp.t' );
9578+
$this->assertEquals( array( (object) array( 'id' => '1' ) ), $result );
9579+
9580+
// SELECT
9581+
$result = $this->assertQuery( 'SELECT * FROM wp.t' );
9582+
$this->assertEquals( array( (object) array( 'id' => '1' ) ), $result );
9583+
9584+
// UPDATE
9585+
$this->assertQuery( 'UPDATE wp.t SET id = 2' );
9586+
$result = $this->assertQuery( 'SELECT * FROM wp.t' );
9587+
$this->assertEquals( array( (object) array( 'id' => '2' ) ), $result );
9588+
9589+
// DELETE
9590+
$this->assertQuery( 'DELETE FROM wp.t' );
9591+
$result = $this->assertQuery( 'SELECT * FROM wp.t' );
9592+
$this->assertCount( 0, $result );
9593+
9594+
// TRUNCATE TABLE
9595+
$this->assertQuery( 'INSERT INTO wp.t (id) VALUES (1)' );
9596+
$this->assertQuery( 'TRUNCATE TABLE wp.t' );
9597+
$result = $this->assertQuery( 'SELECT * FROM wp.t' );
9598+
$this->assertCount( 0, $result );
9599+
9600+
// SHOW CREATE TABLE
9601+
$result = $this->assertQuery( 'SHOW CREATE TABLE wp.t' );
9602+
$this->assertEquals(
9603+
array(
9604+
(object) array(
9605+
'Create Table' => implode(
9606+
"\n",
9607+
array(
9608+
'CREATE TABLE `t` (',
9609+
' `id` int NOT NULL,',
9610+
' PRIMARY KEY (`id`)',
9611+
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci',
9612+
)
9613+
),
9614+
),
9615+
),
9616+
$result
9617+
);
9618+
9619+
// SHOW COLUMNS
9620+
$result = $this->assertQuery( 'SHOW COLUMNS FROM wp.t' );
9621+
$this->assertEquals(
9622+
array(
9623+
(object) array(
9624+
'Field' => 'id',
9625+
'Type' => 'int',
9626+
'Null' => 'NO',
9627+
'Key' => 'PRI',
9628+
'Default' => null,
9629+
'Extra' => '',
9630+
),
9631+
),
9632+
$result
9633+
);
9634+
9635+
// SHOW COLUMNS with both qualified table name and "FROM database" clause.
9636+
// In case both are present, the "FROM database" clause takes precedence.
9637+
$result = $this->assertQuery( 'SHOW COLUMNS FROM information_schema.t FROM wp' );
9638+
$this->assertEquals(
9639+
array(
9640+
(object) array(
9641+
'Field' => 'id',
9642+
'Type' => 'int',
9643+
'Null' => 'NO',
9644+
'Key' => 'PRI',
9645+
'Default' => null,
9646+
'Extra' => '',
9647+
),
9648+
),
9649+
$result
9650+
);
9651+
9652+
// SHOW INDEXES
9653+
$result = $this->assertQuery( 'SHOW INDEXES FROM wp.t' );
9654+
$this->assertCount( 1, $result );
9655+
$this->assertEquals( 'PRIMARY', $result[0]->Key_name );
9656+
9657+
// SHOW INDEXES with both qualified table name and "FROM database" clause.
9658+
// In case both are present, the "FROM database" clause takes precedence.
9659+
$result = $this->assertQuery( 'SHOW INDEXES FROM information_schema.t FROM wp' );
9660+
$this->assertCount( 1, $result );
9661+
$this->assertEquals( 'PRIMARY', $result[0]->Key_name );
9662+
9663+
// DESCRIBE
9664+
$result = $this->assertQuery( 'DESCRIBE wp.t' );
9665+
$this->assertCount( 1, $result );
9666+
$this->assertEquals( 'id', $result[0]->Field );
9667+
$this->assertEquals( 'int', $result[0]->Type );
9668+
$this->assertEquals( 'NO', $result[0]->Null );
9669+
$this->assertEquals( 'PRI', $result[0]->Key );
9670+
$this->assertEquals( null, $result[0]->Default );
9671+
$this->assertEquals( '', $result[0]->Extra );
9672+
9673+
// SHOW TABLES
9674+
$result = $this->assertQuery( 'SHOW TABLES FROM wp' );
9675+
$this->assertCount( 1, $result );
9676+
$this->assertEquals( 't', $result[0]->Tables_in_wp );
9677+
9678+
// SHOW TABLE STATUS
9679+
$result = $this->assertQuery( 'SHOW TABLE STATUS FROM wp' );
9680+
$this->assertCount( 1, $result );
9681+
$this->assertEquals( 't', $result[0]->Name );
9682+
9683+
// ALTER TABLE
9684+
$this->assertQuery( 'ALTER TABLE wp.t ADD COLUMN name VARCHAR(255)' );
9685+
$result = $this->assertQuery( 'SHOW COLUMNS FROM wp.t' );
9686+
$this->assertCount( 2, $result );
9687+
9688+
// CREATE INDEX
9689+
$this->assertQuery( 'CREATE INDEX idx_name ON wp.t (name)' );
9690+
$result = $this->assertQuery( 'SHOW INDEXES FROM wp.t' );
9691+
$this->assertCount( 2, $result );
9692+
$this->assertEquals( 'idx_name', $result[1]->Key_name );
9693+
9694+
// DROP INDEX
9695+
$this->assertQuery( 'DROP INDEX idx_name ON wp.t' );
9696+
$result = $this->assertQuery( 'SHOW INDEXES FROM wp.t' );
9697+
$this->assertCount( 1, $result );
9698+
$this->assertEquals( 'PRIMARY', $result[0]->Key_name );
9699+
9700+
// LOCK TABLE
9701+
$this->assertQuery( 'LOCK TABLES wp.t READ' );
9702+
9703+
// UNLOCK TABLE
9704+
$this->assertQuery( 'UNLOCK TABLES' );
9705+
9706+
// ANALYZE TABLE
9707+
$this->assertQuery( 'ANALYZE TABLE wp.t' );
9708+
9709+
// CHECK TABLE
9710+
$this->assertQuery( 'CHECK TABLE wp.t' );
9711+
9712+
// OPTIMIZE TABLE
9713+
$this->assertQuery( 'OPTIMIZE TABLE wp.t' );
9714+
9715+
// REPAIR TABLE
9716+
$this->assertQuery( 'REPAIR TABLE wp.t' );
9717+
9718+
// DROP TABLE
9719+
$this->assertQuery( 'DROP TABLE wp.t' );
9720+
$result = $this->assertQuery( 'SHOW TABLES FROM wp' );
9721+
$this->assertCount( 0, $result );
9722+
}
9723+
9724+
public function testWriteWithUsageOfInformationSchemaTables(): void {
9725+
// Ensure "information_schema.tables" is empty.
9726+
$this->assertQuery( 'DROP TABLE _options, _dates' );
9727+
$result = $this->assertQuery( 'SELECT * FROM information_schema.tables' );
9728+
$this->assertCount( 0, $result );
9729+
9730+
// Create a table.
9731+
$this->assertQuery( 'CREATE TABLE t (id INT, value VARCHAR(255))' );
9732+
9733+
// INSERT with SELECT from information schema.
9734+
$this->assertQuery( 'INSERT INTO t (id, value) SELECT 1, table_name FROM information_schema.tables' );
9735+
$result = $this->assertQuery( 'SELECT * FROM t' );
9736+
$this->assertCount( 1, $result );
9737+
$this->assertEquals(
9738+
array(
9739+
(object) array(
9740+
'id' => '1',
9741+
'value' => 't',
9742+
),
9743+
),
9744+
$result
9745+
);
9746+
9747+
// INSERT with subselect from information schema.
9748+
$this->assertQuery( 'INSERT INTO t (id, value) SELECT 2, table_name FROM (SELECT table_name FROM information_schema.tables)' );
9749+
$result = $this->assertQuery( 'SELECT * FROM t' );
9750+
$this->assertCount( 2, $result );
9751+
$this->assertEquals(
9752+
array(
9753+
(object) array(
9754+
'id' => '1',
9755+
'value' => 't',
9756+
),
9757+
(object) array(
9758+
'id' => '2',
9759+
'value' => 't',
9760+
),
9761+
),
9762+
$result
9763+
);
9764+
9765+
// INSERT with JOIN on information schema.
9766+
$this->assertQuery(
9767+
'INSERT INTO t (id, value)
9768+
SELECT 3, it.table_name
9769+
FROM information_schema.schemata s
9770+
JOIN information_schema.tables it ON s.schema_name = it.table_schema'
9771+
);
9772+
$result = $this->assertQuery( 'SELECT * FROM t' );
9773+
$this->assertCount( 3, $result );
9774+
$this->assertEquals(
9775+
array(
9776+
(object) array(
9777+
'id' => '1',
9778+
'value' => 't',
9779+
),
9780+
(object) array(
9781+
'id' => '2',
9782+
'value' => 't',
9783+
),
9784+
(object) array(
9785+
'id' => '3',
9786+
'value' => 't',
9787+
),
9788+
),
9789+
$result
9790+
);
9791+
9792+
// TODO: UPDATE with JOIN on information schema is not supported yet.
9793+
9794+
// DELETE with JOIN on information schema.
9795+
$this->assertQuery( 'UPDATE t SET value = "other" WHERE id > 1' );
9796+
$this->assertQuery( 'DELETE t FROM t JOIN information_schema.tables it ON t.value = it.table_name' );
9797+
$result = $this->assertQuery( 'SELECT * FROM t' );
9798+
$this->assertEquals(
9799+
array(
9800+
(object) array(
9801+
'id' => '2',
9802+
'value' => 'other',
9803+
),
9804+
(object) array(
9805+
'id' => '3',
9806+
'value' => 'other',
9807+
),
9808+
),
9809+
$result
9810+
);
9811+
}
95409812
}

0 commit comments

Comments
 (0)