1+ <?php
2+ /*
3+ * Plugin Name: OceanBase Compatibility
4+ * Plugin URI: https://github.com/oceanbase/ecology-plugins/tree/main/wordpress-oceanbase-plugin
5+ * Description: Intercepts and modifies specific SQL DELETE queries targeting wp_options and wp_sitemeta.
6+ * Version: 1.0.1
7+ * Requires at least: 6.1
8+ * Requires PHP: 7.2
9+ * Author: sc-source
10+ * Author URI: https://github.com/sc-source
11+ * License: Apache License Version 2.0
12+ * License URI: http://www.apache.org/licenses/LICENSE-2.0
13+ * Text Domain: oceanbase-compatibility
14+ */
15+ if (!defined ('ABSPATH ' )) {
16+ exit ;
17+ }
18+
19+ class OceanBase_Delete_Expired_Transients {
20+ public function __construct () {
21+ add_filter ('query ' , array ($ this , 'modify_delete_expired_transients ' ));
22+ }
23+ private function build_delete_query ($ original_query , $ results ) {
24+ global $ wpdb ;
25+
26+ $ ids_to_delete = [];
27+ foreach ($ results as $ row ) {
28+ $ ids_to_delete [] = intval ($ row ['a_id ' ]);
29+ $ ids_to_delete [] = intval ($ row ['b_id ' ]);
30+ }
31+
32+ if (!empty ($ ids_to_delete )) {
33+ $ placeholders = implode (', ' , array_fill (0 , count ($ ids_to_delete ), '%d ' ));
34+ $ table = (strpos ($ original_query , $ wpdb ->options ) !== false ) ? $ wpdb ->options : $ wpdb ->sitemeta ;
35+ $ column = (strpos ($ original_query , $ wpdb ->options ) !== false ) ? "option_id " : "meta_id " ;
36+ return $ wpdb ->prepare (
37+ "DELETE FROM $ table WHERE $ column IN ( $ placeholders) " ,
38+ $ ids_to_delete
39+ );
40+ }
41+
42+ return '' ;
43+ }
44+
45+ public function modify_delete_expired_transients ($ query ) {
46+ global $ wpdb ;
47+
48+ $ regex_options = '/^DELETE\s+\w+,\s+\w+\s+FROM\s+ ' . preg_quote ($ wpdb ->options , '/ ' ) . '\s+(\w+),\s+ ' . preg_quote ($ wpdb ->options , '/ ' ) . '\s+(\w+)\s+WHERE\s+ ' .
49+ '.*?a\.option_name\s+LIKE\s+ \'(.+?) \'.*? ' .
50+ '.*?AND\s+a\.option_name\s+NOT\s+LIKE\s+ \'(.+?) \'.*? ' .
51+ '.*?AND\s+b\.option_value\s*<\s*(\d+)\s*$/is ' ;
52+
53+ $ regex_sitemeta = '' ;
54+ if (is_string ($ wpdb ->sitemeta )) {
55+ $ regex_sitemeta = '/^DELETE\s+\w+,\s+\w+\s+FROM\s+ ' . preg_quote ($ wpdb ->sitemeta , '/ ' ) . '\s+(\w+),\s+ ' . preg_quote ($ wpdb ->sitemeta , '/ ' ) . '\s+(\w+)\s+WHERE\s+ ' .
56+ '.*?a\.meta_key\s+LIKE\s+ \'(.+?) \'.*? ' .
57+ '.*?AND\s+a\.meta_key\s+NOT\s+LIKE\s+ \'(.+?) \'.*? ' .
58+ '.*?AND\s+b\.meta_value\s*<\s*(\d+)\s*$/is ' ;
59+ }
60+
61+ if ($ regex_sitemeta && preg_match ($ regex_sitemeta , $ query , $ matches )) {
62+ if (!isset ($ matches [3 ], $ matches [4 ], $ matches [5 ])) {
63+ return $ query ;
64+ }
65+ $ like_pattern = stripslashes ($ matches [3 ]);
66+ $ not_like_pattern = stripslashes ($ matches [4 ]);
67+ $ timeout_value = intval ($ matches [5 ]);
68+
69+ $ clean_pattern = str_replace ('\\' , '' , $ like_pattern );
70+ if (strpos ($ clean_pattern , needle: '_site_transient_ ' ) !== false ) {
71+
72+ $ prepared_query = $ wpdb ->prepare (
73+ "SELECT a.meta_id AS a_id, b.meta_id AS b_id
74+ FROM {$ wpdb ->sitemeta } a, {$ wpdb ->sitemeta } b
75+ WHERE a.meta_key LIKE %s
76+ AND a.meta_key NOT LIKE %s
77+ AND b.meta_key = CONCAT('_site_transient_timeout_', SUBSTRING(a.meta_key, 17))
78+ AND b.meta_value < %d " ,
79+ $ like_pattern , $ not_like_pattern , $ timeout_value
80+ );
81+ }else {
82+ return $ query ;
83+ }
84+ $ results = $ wpdb ->get_results ($ prepared_query , ARRAY_A );
85+ return $ this ->build_delete_query ($ query , $ results );
86+ }
87+
88+
89+ if (preg_match ($ regex_options , $ query , $ matches )) {
90+
91+ if (!isset ($ matches [3 ], $ matches [4 ], $ matches [5 ])) {
92+ return $ query ;
93+ }
94+ $ like_pattern = stripslashes ($ matches [3 ]);
95+ $ not_like_pattern = stripslashes ($ matches [4 ]);
96+ $ timeout_value = intval ($ matches [5 ]);
97+
98+ $ clean_pattern = str_replace ('\\' , '' , $ like_pattern );
99+ if (strpos ($ clean_pattern , needle: '_site_transient_ ' ) !== false ) {
100+
101+ $ prepared_query = $ wpdb ->prepare (
102+ "SELECT a.option_id AS a_id, b.option_id AS b_id
103+ FROM {$ wpdb ->options } a, {$ wpdb ->options } b
104+ WHERE a.option_name LIKE %s
105+ AND a.option_name NOT LIKE %s
106+ AND b.option_name = CONCAT('_site_transient_timeout_', SUBSTRING(a.option_name, 17))
107+ AND b.option_value < %d " ,
108+ $ like_pattern , $ not_like_pattern , $ timeout_value
109+ );
110+ }elseif (strpos ($ clean_pattern , needle: '_transient_ ' ) !== false ) {
111+
112+ $ prepared_query = $ wpdb ->prepare (
113+ "SELECT a.option_id AS a_id, b.option_id AS b_id
114+ FROM {$ wpdb ->options } a, {$ wpdb ->options } b
115+ WHERE a.option_name LIKE %s
116+ AND a.option_name NOT LIKE %s
117+ AND b.option_name = CONCAT('_transient_timeout_', SUBSTRING(a.option_name, 12))
118+ AND b.option_value < %d " ,
119+ $ like_pattern , $ not_like_pattern , $ timeout_value
120+ );
121+ } else {
122+ return $ query ;
123+ }
124+
125+ $ results = $ wpdb ->get_results ($ prepared_query , ARRAY_A );
126+ return $ this ->build_delete_query ($ query , $ results );
127+ }
128+ return $ query ;
129+
130+ }
131+ }
132+
133+ // Initialize the plugin
134+ new OceanBase_Delete_Expired_Transients ();
0 commit comments