Skip to content

Commit bef8c6d

Browse files
committed
Add support for table LOCK and UNLOCK statements
1 parent 2408bd6 commit bef8c6d

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

tests/WP_SQLite_Driver_Tests.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6238,4 +6238,60 @@ public function testUserVariables(): void {
62386238
$result = $this->assertQuery( 'SELECT @my_var' );
62396239
$this->assertEquals( 3, $result[0]->{'@my_var'} );
62406240
}
6241+
6242+
public function testLockingStatements(): void {
6243+
$this->assertQuery( 'CREATE TABLE t (id INT)' );
6244+
6245+
// When there is no lock, UNLOCK statement shouldn't fail.
6246+
$this->assertQuery( 'UNLOCK TABLES' );
6247+
6248+
// READ LOCK.
6249+
$this->assertQuery( 'LOCK TABLES t READ' );
6250+
$this->assertQuery( 'UNLOCK TABLES' );
6251+
6252+
// WRITE LOCK.
6253+
$this->assertQuery( 'LOCK TABLES t WRITE' );
6254+
$this->assertQuery( 'UNLOCK TABLES' );
6255+
6256+
// LOCK inside a transaction.
6257+
$this->assertQuery( 'BEGIN' );
6258+
$this->assertQuery( 'LOCK TABLES t WRITE' );
6259+
$this->assertQuery( 'UNLOCK TABLES' );
6260+
$this->assertQuery( 'COMMIT' );
6261+
6262+
// Transaction inside LOCK statements.
6263+
$this->assertQuery( 'LOCK TABLES t WRITE' );
6264+
$this->assertQuery( 'BEGIN' );
6265+
$this->assertQuery( 'COMMIT' );
6266+
$this->assertQuery( 'UNLOCK TABLES' );
6267+
}
6268+
6269+
public function testLockNonExistentTableForRead(): void {
6270+
$this->expectException( 'WP_SQLite_Driver_Exception' );
6271+
$this->expectExceptionMessage( "Table 'wp.t' doesn't exist" );
6272+
$this->assertQuery( 'LOCK TABLES t READ' );
6273+
}
6274+
6275+
public function testLockNonExistentTableForWrite(): void {
6276+
$this->expectException( 'WP_SQLite_Driver_Exception' );
6277+
$this->expectExceptionMessage( "Table 'wp.t' doesn't exist" );
6278+
$this->assertQuery( 'LOCK TABLES t WRITE' );
6279+
}
6280+
6281+
public function testLockMultipleWithNonExistentTable(): void {
6282+
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
6283+
$this->assertQuery( 'CREATE TABLE t3 (id INT)' );
6284+
6285+
$this->expectException( 'WP_SQLite_Driver_Exception' );
6286+
$this->expectExceptionMessage( "Table 'wp.t2' doesn't exist" );
6287+
$this->assertQuery( 'LOCK TABLES t1 READ, t2 READ, t3 WRITE' );
6288+
}
6289+
6290+
public function testLockTemporaryTables(): void {
6291+
$this->assertQuery( 'CREATE TEMPORARY TABLE t1 (id INT)' );
6292+
$this->assertQuery( 'CREATE TABLE t2 (id INT)' );
6293+
$this->assertQuery( 'CREATE TEMPORARY TABLE t3 (id INT)' );
6294+
$this->assertQuery( 'LOCK TABLES t1 READ, t2 READ, t3 WRITE' );
6295+
$this->assertQuery( 'UNLOCK TABLES' );
6296+
}
62416297
}

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,16 @@ class WP_SQLite_Driver {
399399
*/
400400
private $transaction_level = 0;
401401

402+
/**
403+
* Whether a MySQL table lock is active.
404+
*
405+
* Set to "true" when a lock is acquired using the MySQL LOCK statement.
406+
* Set to "false" when locks are released using the MySQL UNLOCK statement.
407+
*
408+
* @var bool
409+
*/
410+
private $table_lock_active = false;
411+
402412
/**
403413
* The PDO fetch mode used for the emulated query.
404414
*
@@ -1026,6 +1036,60 @@ private function execute_transaction_or_locking_statement( WP_Parser_Node $node
10261036
break;
10271037
}
10281038

1039+
// Unknown statement. Fall through to the default case.
1040+
case 'lockStatement':
1041+
// LOCK TABLE/LOCK TABLES.
1042+
if (
1043+
WP_MySQL_Lexer::LOCK_SYMBOL === $token->id
1044+
&& $subnode->has_child_node( 'lockItem' )
1045+
) {
1046+
// Check if the table(s) exists.
1047+
$lock_items = $subnode->get_child_nodes( 'lockItem' );
1048+
foreach ( $lock_items as $lock_item ) {
1049+
$table_name = $this->unquote_sqlite_identifier(
1050+
$this->translate( $lock_item->get_first_child_node( 'tableRef' ) )
1051+
);
1052+
try {
1053+
/*
1054+
* Attempt to query the table directly rather than checking
1055+
* SQLite schema or information schema tables, so that we
1056+
* can handle persistent and temporary tables in one query.
1057+
*/
1058+
$this->execute_sqlite_query(
1059+
sprintf( 'SELECT 1 FROM %s LIMIT 0', $table_name )
1060+
);
1061+
} catch ( PDOException $e ) {
1062+
throw $this->new_driver_exception(
1063+
sprintf( "Table '%s.%s' doesn't exist", $this->db_name, $table_name ),
1064+
'42S02'
1065+
);
1066+
}
1067+
}
1068+
1069+
// Start a transaction when no top-level transaction is active.
1070+
if ( 0 === $this->transaction_level ) {
1071+
$this->begin_transaction();
1072+
$this->table_lock_active = true;
1073+
}
1074+
break;
1075+
}
1076+
1077+
// UNLOCK TABLES/UNLOCK TABLE.
1078+
if (
1079+
WP_MySQL_Lexer::UNLOCK_SYMBOL === $token->id
1080+
&& (
1081+
$subnode->has_child_token( WP_MySQL_Lexer::TABLE_SYMBOL )
1082+
|| $subnode->has_child_token( WP_MySQL_Lexer::TABLES_SYMBOL )
1083+
)
1084+
) {
1085+
// Commit the transaction when created by the LOCK statement.
1086+
if ( 1 === $this->transaction_level && $this->table_lock_active ) {
1087+
$this->commit();
1088+
$this->table_lock_active = false;
1089+
}
1090+
break;
1091+
}
1092+
10291093
// Unknown statement. Fall through to the default case.
10301094
default:
10311095
throw $this->new_not_supported_exception(

0 commit comments

Comments
 (0)