From d0e6d92fb0cb7bbbf9ccfce50e510727bace0626 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 21 Sep 2016 10:44:15 -0700 Subject: [PATCH] Check Database::mSessionTempTables in Database::tableExists() Also make the temp table tracking catch plain "DROP TABLE" in addition to the stricter "DROP TEMPORARY TABLE" clause. Bug: T146300 Change-Id: Ia8306ec25e63adcdcf0dcc8f6a700dd01afdc948 --- includes/libs/rdbms/database/Database.php | 24 ++++++++---- .../libs/rdbms/database/DatabaseMysqlBase.php | 4 ++ tests/phpunit/includes/db/DatabaseSQLTest.php | 38 +++++++++++++++++++ .../includes/db/DatabaseTestHelper.php | 5 +++ 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 9993277217..908e852339 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -204,7 +204,7 @@ abstract class Database implements IDatabase, LoggerAwareInterface { /** @var array Map of (name => 1) for locks obtained via lock() */ private $mNamedLocksHeld = []; /** @var array Map of (table name => 1) for TEMPORARY tables */ - private $mSessionTempTables = []; + protected $mSessionTempTables = []; /** @var IDatabase|null Lazy handle to the master DB this server replicates from */ private $lazyMasterHandle; @@ -795,16 +795,19 @@ abstract class Database implements IDatabase, LoggerAwareInterface { */ protected function registerTempTableOperation( $sql ) { if ( preg_match( - '/^(CREATE|DROP)\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', + '/^CREATE\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', $sql, $matches ) ) { - list( , $verb, $table ) = $matches; - if ( $verb === 'CREATE' ) { - $this->mSessionTempTables[$table] = 1; - } else { - unset( $this->mSessionTempTables[$table] ); - } + $this->mSessionTempTables[$matches[1]] = 1; + + return true; + } elseif ( preg_match( + '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', + $sql, + $matches + ) ) { + unset( $this->mSessionTempTables[$matches[1]] ); return true; } elseif ( preg_match( @@ -1400,6 +1403,11 @@ abstract class Database implements IDatabase, LoggerAwareInterface { } public function tableExists( $table, $fname = __METHOD__ ) { + $tableRaw = $this->tableName( $table, 'raw' ); + if ( isset( $this->mSessionTempTables[$tableRaw] ) ) { + return true; // already known to exist + } + $table = $this->tableName( $table ); $old = $this->ignoreErrors( true ); $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname ); diff --git a/includes/libs/rdbms/database/DatabaseMysqlBase.php b/includes/libs/rdbms/database/DatabaseMysqlBase.php index 39cf5bcefd..7f67b6e92a 100644 --- a/includes/libs/rdbms/database/DatabaseMysqlBase.php +++ b/includes/libs/rdbms/database/DatabaseMysqlBase.php @@ -520,6 +520,10 @@ abstract class DatabaseMysqlBase extends DatabaseBase { function tableExists( $table, $fname = __METHOD__ ) { $table = $this->tableName( $table, 'raw' ); + if ( isset( $this->mSessionTempTables[$table] ) ) { + return true; // already known to exist and won't show in SHOW TABLES anyway + } + $encLike = $this->buildLike( $table ); return $this->query( "SHOW TABLES $encLike", $fname )->numRows() > 0; diff --git a/tests/phpunit/includes/db/DatabaseSQLTest.php b/tests/phpunit/includes/db/DatabaseSQLTest.php index 0013685184..68bc964e8c 100644 --- a/tests/phpunit/includes/db/DatabaseSQLTest.php +++ b/tests/phpunit/includes/db/DatabaseSQLTest.php @@ -827,4 +827,42 @@ class DatabaseSQLTest extends MediaWikiTestCase { ], ]; } + + public function testSessionTempTables() { + $temp1 = $this->database->tableName( 'tmp_table_1' ); + $temp2 = $this->database->tableName( 'tmp_table_2' ); + $temp3 = $this->database->tableName( 'tmp_table_3' ); + + $this->database->query( "CREATE TEMPORARY TABLE $temp1 LIKE orig_tbl", __METHOD__ ); + $this->database->query( "CREATE TEMPORARY TABLE $temp2 LIKE orig_tbl", __METHOD__ ); + $this->database->query( "CREATE TEMPORARY TABLE $temp3 LIKE orig_tbl", __METHOD__ ); + + $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) ); + $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) ); + $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) ); + + $this->database->dropTable( 'tmp_table_1', __METHOD__ ); + $this->database->dropTable( 'tmp_table_2', __METHOD__ ); + $this->database->dropTable( 'tmp_table_3', __METHOD__ ); + + $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) ); + $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) ); + $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) ); + + $this->database->query( "CREATE TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ ); + $this->database->query( "CREATE TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ ); + $this->database->query( "CREATE TEMPORARY TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ ); + + $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) ); + $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) ); + $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) ); + + $this->database->query( "DROP TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ ); + $this->database->query( "DROP TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ ); + $this->database->query( "DROP TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ ); + + $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) ); + $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) ); + $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) ); + } } diff --git a/tests/phpunit/includes/db/DatabaseTestHelper.php b/tests/phpunit/includes/db/DatabaseTestHelper.php index caa29bd448..2ee859feaa 100644 --- a/tests/phpunit/includes/db/DatabaseTestHelper.php +++ b/tests/phpunit/includes/db/DatabaseTestHelper.php @@ -94,6 +94,11 @@ class DatabaseTestHelper extends DatabaseBase { } public function tableExists( $table, $fname = __METHOD__ ) { + $tableRaw = $this->tableName( $table, 'raw' ); + if ( isset( $this->mSessionTempTables[$tableRaw] ) ) { + return true; // already known to exist + } + $this->checkFunctionName( $fname ); return in_array( $table, (array)$this->tablesExists ); -- 2.20.1