前提
WordPress特有の仕様についての質問になります。
お詳しい方宜しくお願い致します。
実現したいこと
2つのテーブルを 「厳密に」 LOCK TABLES
し、ROLLBACK
したいです。
「厳密に」 とは 「一方のテーブルだけが更新されることを絶対になくしたい(2つのテーブルを絶対に一緒に更新したい)」 という意味です。
一般的に$wpdb->last_error
の場合に自ら throw~catch し、ROLLBACK
させると思います。
今回はPHPのNoticeエラーの場合でも throw~catch し、ROLLBACK
させたいのです。
発生している問題
該当のソースコードのように、Noticeエラーの場合でもそれを throw~catch させようしてset_error_handler
を書きました。
しかしこうすると次の errorMessage のように、なぜかwp_options
やwp_comments
のLOCK TABLES
を要求されます。
(どこにもwp_options
やwp_comments
を更新するような処理は書いていないのに、です。)
errorMessage
[13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_plugins' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query [13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_themes' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query [13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_core' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, wp_get_translation_updates, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query [13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_comments' was not locked with LOCK TABLES for query SELECT comment_approved, COUNT( * ) AS total FROM wp_comments GROUP BY comment_approved made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_comments_menu, wp_count_comments, get_comment_count, QM_DB->query
該当のソースコード
ロック対象の2つのテーブルです。text a
やtag a
という値が入っています。
問題のソースコードは後述のupdate()
で生じます。
SQL
CREATE TABLE my_texts ( ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT ,text VARCHAR(10) NOT NULL ,PRIMARY KEY (ID) ); INSERT INTO my_texts (text) VALUES ('text a'); CREATE TABLE my_tags ( ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT ,tag VARCHAR(10) NOT NULL ,PRIMARY KEY (ID) ); INSERT INTO my_tags (tag) VALUES ('tag a');
以下がupdate()
です。
上記のtext a
やtag a
を、text b
やtag b
へ更新しようとしますが、11行目のtag_data
のキーがid
でなくxx
であるために、59行目で「Notice: Undefined index」を生じさせています。
このエラーを throw~catch させるべく、set_error_handler
を書いたのですが、すると上記の errorMessage が表示されてしまうのが問題です。
PHP
// PHPのNoticeエラーの場合でも throw~catch function exceptions_error_handler($severity, $message, $filename, $lineno) { throw new ErrorException($message, 0, $severity, $filename, $lineno); } // 更新データ $post_data = [ 'id' => 1, 'text' => 'text b', 'tag_data' => [ 'xx' => 1, // 「Notice: Undefined index」 'tag' => 'tag b' ] ]; // 更新を実行 $posted = update( $post_data ); var_dump( $posted ); // 更新の関数 function update( $post_data ){ global $wpdb; // PHPのNoticeエラーの場合でも throw~catch set_error_handler('exceptions_error_handler'); $result = ['status'=>'error']; try { $is_error = null; // テーブルのロック $wpdb->query('SET autocommit=0'); $sql = 'LOCK TABLES my_texts WRITE, my_tags WRITE'; if ( $wpdb->query($sql) === false ) { $is_error = true; $mes = 'テーブルのロックで失敗'; error_log( $mes . ' $sql = ' . json_encode($sql) ); throw new Exception( $mes ); } else { // my_texts の投稿 $sql = 'UPDATE my_texts SET text = %s WHERE ID IN ( %d )'; $prepares = [ $post_data['text'], $post_data['id'] ]; $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$prepares ) ); if ( $wpdb->last_error ) { $is_error = true; $mes = 'my_texts の get_results で失敗'; error_log( $mes . ' $sql = ' . json_encode($sql) ); throw new Exception( $mes ); } else { // タグ情報 $tag_data = $post_data['tag_data']; // my_tags の投稿 $sql = 'UPDATE my_tags SET tag = %s WHERE ID IN ( %d )'; $prepares = [ $tag_data['tag'], $tag_data['id'] ]; // 「Notice: Undefined index」 $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$prepares ) ); if( $wpdb->last_error ){ $is_error = true; $mes = 'my_tags の get_results で失敗'; error_log( $mes . ' $sql = ' . json_encode($sql) ); throw new Exception( $mes ); }else{ $result = ['status'=>'ok']; } } } // ロック解除 if( $is_error ){ $wpdb->query('ROLLBACK'); $wpdb->query('UNLOCK TABLES'); error_log( 'ROLLBACK しました' ); }else{ $wpdb->query('COMMIT'); $wpdb->query('UNLOCK TABLES'); error_log( 'COMMIT しました' ); } } catch (Exception $e) { $result = $e->getMessage(); } finally { restore_error_handler(); } return $result; }
試したこと
errorMessage のいう事を素直に聞いてwp_options
やwp_comments
のLOCK TABLES
を次のように実行すると、errorMessage はなくなりました。
PHP
$sql = 'LOCK TABLES my_texts WRITE, my_tags WRITE, wp_options WRITE, wp_comments WRITE';
しかし、そもそもwp_options
やwp_comments
のLOCK TABLES
がなぜ必要なのかわからないので、これであっているのか?とても不安な心境です。
そこで、2つのテーブルを 「厳密に」 LOCK TABLES
するような方法として、適切な処理を求めて質問させて頂きました。
PHPのNoticeエラーの場合でもROLLBACK
させるにあたっては、どのような処理が良いのでしょうか?
長い質問で申し訳ございませんが、どなたかWordPressにお詳しい方がいらっしゃいましたら何卒、宜しくお願い致します。
理由不明だが解決した方法(2022/03/14 23:09)
上記「// ロック解除」の処理を下記のようにしたところ、 「厳密に」 LOCK TABLES
もでき、errorMessage もなくなりました。しかしなぜ解決したのか?がわからずやはり不安な心境です。正しい処理なのか、他に問題はないのか、大いに疑問です。
原因がお分かりになる方いらっしゃいましたらご教示頂けませんでしょうか。
■ 上記「// ロック解除」の処理↓
php
// ロック解除 if( $is_error ){ $wpdb->query('ROLLBACK'); $wpdb->query('UNLOCK TABLES'); error_log( 'ROLLBACK しました' ); }else{ $wpdb->query('COMMIT'); $wpdb->query('UNLOCK TABLES'); error_log( 'COMMIT しました' ); } } catch (Exception $e) { $result = $e->getMessage(); } finally { restore_error_handler(); } return $result; }
■ 理由不明だが解決した方法↓
php
// ロック解除 $wpdb->query('COMMIT'); error_log( 'COMMIT しました' ); } catch (Exception $e) { $result = $e->getMessage(); $wpdb->query('ROLLBACK'); error_log( 'ROLLBACK しました' ); } finally { restore_error_handler(); $wpdb->query('UNLOCK TABLES'); } return $result; }
まだ回答がついていません
会員登録して回答してみよう