Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion packages/mysql-on-sqlite/src/sqlite/class-wp-mysql-on-sqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -2886,12 +2886,27 @@ private function execute_delete_statement( WP_Parser_Node $node ): void {
$affected_rows = 0;
if ( count( $ids ) > 0 ) {
foreach ( $table_aliases as $table ) {
/*
* With an outer JOIN, a table may have no matching row for
* some of the selected rows. Its ROWID is NULL for those rows,
* and there is nothing to delete from the table for them.
*/
$rowids = array_filter(
array_column( $ids, "{$table}_rowid" ),
function ( $rowid ) {
return null !== $rowid;
}
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
if ( count( $rowids ) === 0 ) {
continue;
}

$stmt = $this->execute_sqlite_query(
sprintf(
'DELETE FROM %s AS %s WHERE rowid IN ( %s )',
$this->quote_sqlite_identifier( $alias_map[ $table ] ),
$this->quote_sqlite_identifier( $table ),
implode( ', ', array_column( $ids, "{$table}_rowid" ) )
implode( ', ', $rowids )
)
);
$affected_rows += $stmt->rowCount();
Expand Down
38 changes: 38 additions & 0 deletions packages/mysql-on-sqlite/tests/WP_MySQL_On_SQLite_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -6200,6 +6200,44 @@ public function testTranslatesComplexDelete() {
);
}

public function testMultiTableDeleteWithLeftJoinSkipsUnmatchedRows(): void {
$this->assertQuery( 'CREATE TABLE p (id INT PRIMARY KEY, type TEXT)' );
$this->assertQuery( 'CREATE TABLE pm (id INT PRIMARY KEY, post_id INT)' );
$this->assertQuery( "INSERT INTO p VALUES (1, 'revision'), (2, 'revision'), (3, 'post')" );
$this->assertQuery( 'INSERT INTO pm VALUES (1, 1), (2, 1), (3, 3)' );

// Post 2 has no metadata, so its "pm" ROWID is NULL.
$result = $this->assertQuery(
"DELETE p, pm FROM p LEFT JOIN pm ON pm.post_id = p.id WHERE p.type = 'revision'"
);
$this->assertSame( 4, $result );

$result = $this->assertQuery( 'SELECT id FROM p ORDER BY id' );
$this->assertSame( array( '3' ), array_column( $result, 'id' ) );
$result = $this->assertQuery( 'SELECT id FROM pm ORDER BY id' );
$this->assertSame( array( '3' ), array_column( $result, 'id' ) );
}

public function testMultiTableDeleteWithLeftJoinAndNoMatchesInJoinedTable(): void {
$this->assertQuery( 'CREATE TABLE p (id INT PRIMARY KEY)' );
$this->assertQuery( 'CREATE TABLE pm (id INT PRIMARY KEY, post_id INT)' );
$this->assertQuery( 'INSERT INTO p VALUES (1), (2), (3)' );
$this->assertQuery( 'INSERT INTO pm VALUES (1, 3)' );

// Only the joined table is a target, and it has no matching rows.
$result = $this->assertQuery( 'DELETE pm FROM p LEFT JOIN pm ON pm.post_id = p.id WHERE p.id < 3' );
$this->assertSame( 0, $result );

// Both tables are targets, and the joined table has no matching rows.
$result = $this->assertQuery( 'DELETE p, pm FROM p LEFT JOIN pm ON pm.post_id = p.id WHERE p.id < 3' );
$this->assertSame( 2, $result );

$result = $this->assertQuery( 'SELECT id FROM p ORDER BY id' );
$this->assertSame( array( '3' ), array_column( $result, 'id' ) );
$result = $this->assertQuery( 'SELECT id FROM pm ORDER BY id' );
$this->assertSame( array( '1' ), array_column( $result, 'id' ) );
}

public function testDeleteReturnsZeroAffectedRowsWhenNoMatchingRows() {
$this->assertQuery( 'CREATE TABLE t (id INT)' );
$this->assertQuery( 'INSERT INTO t (id) VALUES (1)' );
Expand Down
Loading