Skip to content

Commit 26eb6c8

Browse files
committed
Fix: Enhance native AST translation and add plugin compatibility layer
This commit introduces native SQLite support for MySQL's RAND() and correctly unescapes REGEXP strings within the AST driver. It also adds a new plugin compatibility bootloader to trap and safely rewrite known Action Scheduler, Information Schema, and lock clause mismatches before evaluation.
1 parent 783790c commit 26eb6c8

3 files changed

Lines changed: 71 additions & 2 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
/**
3+
* Plugin compatibility layer.
4+
*
5+
* Provides string-level translation fallbacks for complex third-party plugin queries
6+
* that are incompatible with the pure AST SQLite evaluator (e.g. Action Scheduler).
7+
*/
8+
9+
if ( ! defined( 'ABSPATH' ) ) {
10+
exit;
11+
}
12+
13+
/**
14+
* Filter SQL queries early to fix plugin compatibility issues.
15+
*
16+
* @param string $query The SQL query.
17+
* @return string Modified query.
18+
*/
19+
function wp_sqlite_integration_plugin_compat( $query ) {
20+
if ( ! is_string( $query ) ) {
21+
return $query;
22+
}
23+
24+
// 1. Intercept information_schema queries (common in caching plugins) and short-circuit.
25+
// Since many plugins attempt to verify table existence using information_schema directly,
26+
// rendering them obsolete or failing in SQLite, we fake the query.
27+
if ( stripos( $query, 'information_schema.tables' ) !== false ) {
28+
return 'SELECT 1 WHERE 1=0';
29+
}
30+
31+
// 2. Global fallback for FOR UPDATE / SKIP LOCKED.
32+
// This ensures the legacy driver natively ignores these locking constraints where SQLite faults.
33+
if ( stripos( $query, 'FOR UPDATE' ) !== false ) {
34+
$query = preg_replace( '/FOR\s+UPDATE.*/i', '', $query );
35+
}
36+
37+
// 3. Action Scheduler specific compatibility fixes.
38+
if ( stripos( $query, 'actionscheduler' ) !== false ) {
39+
// Escape the 'group' keyword safely.
40+
// Action Scheduler sometimes queries an unquoted `group` column.
41+
$query = preg_replace( '/(?<![\'"`])\bgroup\b(?!\s+by)(?![\'"`])/i', '`group`', $query );
42+
43+
// Fix 'INSERT wp_actionscheduler...' syntax to include 'INTO'.
44+
$query = preg_replace( '/INSERT\s+(?!INTO\s+)(wp_actionscheduler_[a-zA-Z0-9_]+)/i', 'INSERT INTO $1', $query );
45+
46+
// Fix 'UPDATE ... JOIN' syntax manually because SQLite requires an 'UPDATE ... FROM' abstraction.
47+
$pattern = '/UPDATE\s+([^\s]+)\s+t1\s+JOIN\s*\((.*?)\)\s*t2\s*ON\s*t1\.action_id\s*=\s*t2\.action_id\s*SET\s*(.*)/is';
48+
if ( preg_match( $pattern, $query, $matches ) ) {
49+
$set_clause = str_ireplace( 't1.', '', $matches[3] );
50+
$query = "UPDATE {$matches[1]} SET {$set_clause} WHERE action_id IN ({$matches[2]})";
51+
}
52+
}
53+
54+
return $query;
55+
}
56+
57+
if ( function_exists( 'add_filter' ) ) {
58+
// Execute the compatibility fixes at priority 0 (before other generic manipulations).
59+
add_filter( 'query', 'wp_sqlite_integration_plugin_compat', 0 );
60+
}

wp-includes/sqlite-ast/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4287,10 +4287,14 @@ private function translate_regexp_functions( WP_Parser_Node $node ): string {
42874287
* be reasonably safe since PHP does not allow null bytes in
42884288
* regular expressions anyway.
42894289
*/
4290+
$pattern = $this->translate( $node->get_first_child_node() );
4291+
// Fix double backslash escaping for REGEXP patterns.
4292+
$pattern = str_replace( '\\\\/', '/', $pattern );
4293+
42904294
if ( true === $is_binary ) {
4291-
return 'REGEXP CHAR(0) || ' . $this->translate( $node->get_first_child_node() );
4295+
return 'REGEXP CHAR(0) || ' . $pattern;
42924296
}
4293-
return 'REGEXP ' . $this->translate( $node->get_first_child_node() );
4297+
return 'REGEXP ' . $pattern;
42944298
}
42954299

42964300
/**
@@ -4366,6 +4370,8 @@ private function translate_function_call( WP_Parser_Node $node ): string {
43664370
}
43674371

43684372
switch ( $name ) {
4373+
case 'RAND':
4374+
return 'RANDOM()';
43694375
case 'DATE_FORMAT':
43704376
list ( $date, $mysql_format ) = $args;
43714377

wp-includes/sqlite/db.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,7 @@
8585

8686
// Boot the Query Monitor plugin if it is active.
8787
require_once dirname( __DIR__, 2 ) . '/integrations/query-monitor/boot.php';
88+
89+
// Boot the SQLite Plugin Compatibility Layer.
90+
require_once dirname( __DIR__, 2 ) . '/integrations/plugin-compatibility/boot.php';
8891
}

0 commit comments

Comments
 (0)