如果 MySQL 儲存過程包含成功的查詢,則不會引發錯誤
我正在編寫一個簡單的 MySQL 儲存過程,並使用 PDO 從 PHP 呼叫它。
這很簡單,足以說明我遇到的問題。
這個程序
test_procedure_2
,不會拋出任何錯誤。CREATE PROCEDURE `test_procedure_1`() BEGIN select 'gary'; CALL raise_error; END
它返回結果集:
[0] => Array ( [0] => Array ( [gary] => gary ) )
然而…
CREATE PROCEDURE `test_procedure_2`() BEGIN CALL raise_error; select 'gary'; END
…這個過程
test_procedure_2
,當被呼叫時,實際上會拋出一個帶有消息的 PDOException"SQLSTATE[42000]: Syntax error or access violation: 1305 PROCEDURE vjs_admin_dev.raise_error does not exist"
。並且select 'gary'
查詢永遠不會被執行。我喜歡。為什麼第一個過程在明顯包含錯誤時沒有拋出錯誤?我想提出一個儲存過程,每當其中任何地方出現錯誤時,它都會拋出錯誤。謝謝你的幫助!
我正在使用
- Ubuntu:16.04 和
- PHP: 7.0.15
- mysql:版本 14.14 Distrib 5.7.21,適用於 Linux (x86_64),使用 EditLine 包裝器
編輯:我已經用 mysqli 和 PDO 嘗試過這個,我得到了相同的結果。以下是一些顯示我的問題的簡單程式碼範例:
- Mysqli 版本:https ://pastebin.com/rATMST4H
- PDO 版本:https ://pastebin.com/Wiq3yFmQ
Mysqli 版本的輸出是:
Running test_procedure_1 CREATE PROCEDURE `test_procedure_1`()BEGIN select 'gary'; CALL raise_error;END RESULTS:Array ( [gary] => gary ) ---------- Running test_procedure_2 CREATE PROCEDURE `test_procedure_2`()BEGIN CALL raise_error; select 'gary';END CALL failed: (1305) PROCEDURE vjs_admin_dev.raise_error does not exist ----------
PDO 版本的輸出為:
Running test_procedure_1 CREATE PROCEDURE `test_procedure_1`()BEGIN select 'gary'; CALL raise_error;END RESULTS:Array ( [gary] => gary [0] => gary ) ---------- Running test_procedure_2 CREATE PROCEDURE `test_procedure_2`()BEGIN CALL raise_error; select 'gary';END CALL failed: (42000) Array ( [0] => 42000 [1] => 1305 [2] => PROCEDURE vjs_admin_dev.raise_error does not exist ) ----------
哇,這是一個相關的問題,因為您無法辨識過程中的錯誤,並且 this 通過隱藏在一些有效結果旁邊!我認為你的問題應該更受歡迎。
經過多次嘗試和測試,我找到了解決它的方法。您希望循環遍歷過程結果集,直到沒有更多結果可用(PHP 手動方式)。但是,我發現了一個替代控制流來擷取先前有效結果之後的錯誤:
<?php mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); try { $sql="CALL procedureWithErrors()"; $conn=new mysqli($host,$user,$pass,$db); /*The usual way (does not work)*/ $conn->multi_query($sql); do{ $result=$conn->store_result(); if($result){ while($row=$result->fetch_assoc()){ print_r($row); } $result->free(); } }while($conn->more_results() && $conn->next_result()); /*This way works*/ $conn->multi_query($sql); while(($result=$conn->store_result())!==FALSE){ if($result){ while($row=$result->fetch_assoc()){ print_r($row); } $result->free(); } $conn->next_result(); } /*Just closing and error handling, not relevant code from here*/ $conn->close(); }catch(mysqli_sql_exception | Exception $e){ $error="Error #".$e->getCode()." ".$e->getMessage().PHP_EOL; if(isset($conn) && get_class($e)=="mysqli_sql_exception") $error.="SQLSTATE #".$conn->sqlstate." Statement: $sql".PHP_EOL; $error.=$e->getTraceAsString(); echo(nl2br($error)); }finally{ if(isset($result) && $result instanceof mysql_result) $result->free(); if(isset($conn) && is_resource($conn) && get_resource_type($conn)==='mysql link') $conn->close(); } ?>
問題似乎就是這樣,
mysqli::more_results
如果mysql::next_result
發生錯誤則返回 FALSE,因此他們無法區分錯誤和缺乏進一步的結果。
那是不可能的。您不能根據後面的語句是否會在儲存過程中引發錯誤來阻止語句的執行。
實現類似的唯一方法是這樣的:
drop procedure if exists test_procedure_3; DELIMITER $$ CREATE PROCEDURE `test_procedure_3`() BEGIN if exists (select 1 from information_schema.routines where routine_name = 'raise_error' and routine_schema = database()) then select 'gary'; CALL raise_error; end if; END$$ DELIMITER ; call test_procedure_3;
因此,您只需檢查該程序是否事先存在。沒有其他方法(我嘗試了事務、信號、準備好的語句等等)。