質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

895閲覧

親関数の LOCK TABLES を、子関数のエラーで ROLLBACK させたい

yakan

総合スコア19

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2020/06/29 01:56

編集2020/06/29 02:35

###前提・実現したいこと
親関数の中で実行される子関数でエラーがおきたとき、親関数のLOCK TABLESROLLBACKさせたいです。

###発生している問題
親関数のLOCK TABLESROLLBACKさせることができません。

###該当のソースコード
親関数my_insert_wp_fruitsの中で、子関数my_insert_wp_vegetablesが実行されます。
(それぞれ独自に実行するケースがあるため独立させています。)

さて以下5行目の$valは、子関数の最後に更新されるwp_vegetables2テーブルを更新する値ですが、ここをCREATEで設定したVARCHAR(3)以上の文字にしてエラーを起こしてみました。

しかしROLLBACKが効くのは子関数でLOCK TABLESをかけたwp_vegetables1wp_vegetables2だけで、wp_fruits1wp_fruits2はINSERTが成功してしまうのです。

これを親関数のLOCK TABLESにまでさかのぼって、wp_fruits1wp_fruits2も含めてROLLBACKさせられませんでしょうか?

php

1// 4つのテーブル作成 2create_tables(); 3 4// 実行 5$val = 'val'; // ここを VARCHAR(3) 以上の文字にしてエラーを起こしても、wp_fruits1 と wp_fruits2 は ROLLBACK が効かない 6$fruits_datas = [ 'fruits1'=>['col'=>'val'], 'fruits2'=>['col'=>'val'] ]; 7$vegetable_datas = [ 'vegetables1'=>['col'=>'val'], 'vegetables2'=>['col'=>$val] ]; 8$result = my_insert_wp_fruits($fruits_datas,$vegetable_datas); 9var_dump( $result ); 10 11// 親関数 ( wp_fruits1 と wp_fruits2 を INSERT ) 12function my_insert_wp_fruits($fruits_datas,$vegetable_datas=[]){ 13 global $wpdb; 14 $wpdb->query('SET autocommit=0'); 15 $sql = "LOCK TABLES wp_fruits1 WRITE, wp_fruits2 WRITE"; 16 $wpdb->query($sql); 17 18 $result = null; 19 try { 20 21 // wp_fruits1 を保存 22 $fruits1 = $wpdb->insert( 'wp_fruits1', $fruits_datas['fruits1'], ['%s'] ); 23 if( ! $fruits1 ){ 24 throw new Exception("fruits1 の INSERT でエラー"); 25 }else{ 26 $fruits1_id = $wpdb->insert_id; 27 28 // wp_fruits2 を保存 29 $fruits2 = $wpdb->insert( 'wp_fruits2', $fruits_datas['fruits2'], ['%s'] ); 30 if( ! $fruits2 ){ 31 throw new Exception("fruits2 の INSERT でエラー"); 32 }else{ 33 $fruits2_id = $wpdb->insert_id; 34 35 // 以下の子関数でエラーが出たとき、親関数を ROLLBACK させたい 36 $vegetable_ids = my_insert_wp_vegetables($vegetable_datas); 37 if( isset($vegetable_ids['error']) ){ 38 throw new Exception("my_insert_wp_vegetables でエラー"); 39 } 40 } 41 } 42 43 // 問題なければ 44 $result = ['fruits1_id'=>$fruits1_id,'fruits2_id'=>$fruits2_id]; 45 $result = array_merge( $result, $vegetable_ids ); 46 $wpdb->query('COMMIT'); 47 48 } catch (Exception $e) { 49 $wpdb->query('ROLLBACK'); 50 $result['error'] = $e->getMessage(); 51 } finally { 52 $wpdb->query('UNLOCK TABLES'); 53 } 54 55 return $result; 56} 57 58// 子関数 ( wp_vegetables1 と wp_vegetables2 を INSERT ) 59function my_insert_wp_vegetables($vegetable_datas){ 60 global $wpdb; 61 $wpdb->query('SET autocommit=0'); 62 $sql = "LOCK TABLES wp_vegetables1 WRITE, wp_vegetables2 WRITE"; 63 $wpdb->query($sql); 64 65 $result = null; 66 try { 67 68 // wp_vegetables1 を保存 69 $vegetables1 = $wpdb->insert( 'wp_vegetables1', $vegetable_datas['vegetables1'], ['%s'] ); 70 if( ! $vegetables1 ){ 71 throw new Exception("vegetables1 の INSERT でエラー"); 72 }else{ 73 $vegetables1_id = $wpdb->insert_id; 74 75 // wp_vegetables2 を保存 76 $vegetables2 = $wpdb->insert( 'wp_vegetables2', $vegetable_datas['wp_vegetables2'], ['%s'] ); 77 if( ! $vegetables2 ){ 78 throw new Exception("vegetables2 の INSERT でエラー"); 79 }else{ 80 $vegetables2_id = $wpdb->insert_id; 81 } 82 } 83 84 // 問題なければ 85 $result = ['vegetables1_id'=>$vegetables1_id,'vegetables2_id'=>$vegetables2_id]; 86 $wpdb->query('COMMIT'); 87 88 } catch (Exception $e) { 89 $wpdb->query('ROLLBACK'); 90 $result['error'] = $e->getMessage(); 91 } finally { 92 $wpdb->query('UNLOCK TABLES'); 93 } 94 95 return $result; 96} 97 98// 4つのテーブル作成 99function create_tables(){ 100 global $wpdb; 101 $tables = ['fruits1','fruits2','vegetables2','vegetables1']; 102 foreach( $tables as $table ){ 103 $prefixed_table = $wpdb->prefix . $table; 104 $sql = "CREATE TABLE IF NOT EXISTS $prefixed_table ( 105 ID INT(1) UNSIGNED NOT NULL AUTO_INCREMENT, 106 col VARCHAR(3) NOT NULL, 107 PRIMARY KEY (ID) 108 );"; 109 add_option($table."_version", '1.0'); 110 $wpdb->query($sql); 111 } 112} 113

###試したこと
自分なりの解決策としては、親関数の方で、「もし子関数を更新するならLOCK TABLESを追加」というコードを下記のように加える方法ですが…

こういった方法よりも、親関数にさかのぼってROLLBACKさせたいと思っています。

ご指導宜しくお願い申し上げます。

php

1function my_insert_wp_fruits($fruits_datas,$vegetable_datas=[]){ 2 global $wpdb; 3 $wpdb->query('SET autocommit=0'); 4 $sql = "LOCK TABLES wp_fruits1 WRITE, wp_fruits2 WRITE"; 5 6 // もし子関数を更新するならLOCK TABLESを追加 7 if( !empty($vegetable_datas) ){ 8 $sql .= ",wp_vegetables1 WRITE, wp_vegetables2 WRITE" 9 } 10 11 $wpdb->query($sql); 12 13 // 以下同じ 14}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

MySQL のバージョンがわかりませんが、5.6以降なら大きな差はなかったと思うので...

親関数のLOCK TABLESをROLLBACKさせることができません。

しかしROLLBACKが効くのは子関数でLOCK TABLESをかけたwp_vegetables1とwp_vegetables2だけで、wp_fruits1とwp_fruits2はINSERTが成功してしまうのです。

MySQL ドキュメント

セッションがすでにロックを保持している間にロックを取得するために LOCK TABLES ステートメントを発行した場合は、新しいロックが付与される前に、その既存のロックが暗黙的に解放されます。

と書かれている部分に該当しているので、子関数の LOCK TABLE を実行した時点で、親関数で実行した LOCK TABLE は、暗黙的に解除されて、子関数の LOCK TABLE でロックされます。

つまり、MySQL の仕様で、LOCK TABLE の実行中に LOCK TABLE を呼び出したり、 トランザクション中に LOCK TABLE することはできません。


親関数my_insert_wp_fruitsの中で、子関数my_insert_wp_vegetablesが実行されます。

(それぞれ独自に実行するケースがあるため独立させています。)

の使い分けがわかりませんが、トランザクションを使うのであれば、13.3.1 START TRANSACTION、COMMIT、および ROLLBACK 構文 にあるように

START TRANSACTION; または BEGIN; SQL 文実行 COMMIT; または ROLLBACK;

で、実行可能です。

WordPress の $wpdb を使って記載するなら、こんな感じでしょうか。

$wpdb->query("START TRANSACTION"); try{ $result = $wpdb->insert( $table, $data, $format ); if( is_wp_error( $result ) ){ $wpdb->query('ROLLBACK'); // エラー処理が必要なら書く } else { $wpdb->query('COMMIT'); } }catch( Exception $e ){ $wpdb->query('ROLLBACK'); // エラー処理が必要なら書く }

上記の SQL 処理の部分($wpdb->insert())は、外部関数でも問題無いと思います。
(外部関数の中では、START TRANSACTIONLOCK TABLE は使えません。 使うと今回と同じ問題が発生します。)

投稿2020/06/29 03:43

CHERRY

総合スコア25218

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yakan

2020/06/29 04:05

>つまり、MySQL の仕様で、LOCK TABLE の実行中に LOCK TABLE を呼び出したり、 トランザクション中に LOCK TABLE することはできません。 ありがとうございます。納得いたしました。
CHERRY

2020/06/29 04:35

あれ? 試したのは数年前なので、記憶違いの可能性もあるので、時間のあるときにテスト環境を用意して確認してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問