From 4f2ca28a5ece4442229c23918eed0959e18e1870 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Wed, 6 May 2026 14:36:13 +0200 Subject: [PATCH] Fix W3 Total Cache DbCache fatal in batch_insert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit W3TC's DbCache wrapper reads `$wpdb->col_info` after each cacheable query, which triggers `wpdb::load_col_info()` -> `mysqli_num_fields()`. For non-SELECT queries the result is `true` (bool), so this fatals with "Argument #1 ($result) must be of type mysqli_result, true given". W3TC bypasses caching for write queries via a regex that matches `START TRANSACTION`, `INSERT`, `COMMIT`, etc. — but not `BEGIN`. Switching `BEGIN` to `START TRANSACTION` (semantically identical in MySQL) lets W3TC's regex catch it and skip the col_info path. Reproduced locally with W3TC 2.9.4: identical stack trace with `BEGIN`, no fatal with `START TRANSACTION`. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/class-database.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/class-database.php b/src/class-database.php index 16c5793..3a2ff06 100644 --- a/src/class-database.php +++ b/src/class-database.php @@ -195,8 +195,11 @@ public static function batch_insert( $options, $chunk_size = 500 ) { $table_name = self::get_table_name(); + // Use `START TRANSACTION` (not `BEGIN`) so W3 Total Cache's DbCache layer + // recognizes it as a transaction and skips caching — `BEGIN` slips past its + // regex and triggers a fatal in wpdb::load_col_info() on the boolean result. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching - $wpdb->query( 'BEGIN' ); + $wpdb->query( 'START TRANSACTION' ); foreach ( array_chunk( $options, $chunk_size, true ) as $chunk ) { $values = [];