web-dev-qa-db-ja.com

成功したクエリが含まれている場合、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が呼び出されると、実際には"SQLSTATE[42000]: Syntax error or access violation: 1305 PROCEDURE vjs_admin_dev.raise_error does not exist"というメッセージとともにPDOExceptionがスローされます。また、select 'gary'クエリは実行されません。私はすきです。

明らかにエラーが含まれているのに、最初のプロシージャがエラーをスローしなかったのはなぜですか?私はそれのどこかにエラーがあるときはいつでも、それがエラーを投げるストアドプロシージャを思いつきたいと思います。ご協力いただきありがとうございます!

私は使っています

  • Ubuntu:16.04、および
  • PHP:7.0.15
  • mysql:Ver 14.14 Distrib 5.7.21、Linux(x86_64)、EditLineラッパーを使用

編集:私はこれをmysqliとPDOで試しましたが、同じ結果が得られます。ここに私の問題を示すいくつかの簡単なコード例があります:

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 ) 
----------
3
Gary Reckard

うわー、それは関連する問題です(プロシージャのエラーを特定できないため、これはいくつかの有効な結果の横に非表示を渡します!あなたの質問はもっと人気があると思います。

多くの試行錯誤の末、私はそれを解決する方法を見つけました。使用可能な結果がなくなるまで、プロシージャの結果セットをループ処理することを期待します( 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を返すため、エラーとそれ以上の結果の欠如を区別できません。

1

それは可能ではありません。後続のステートメントがストアドプロシージャでエラーをスローするかどうかに基づいて、ステートメントの実行を妨げることはできません。

同様のことを達成する唯一の方法は、次のようなものです。

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;

したがって、手順が事前に存在するかどうかを確認するだけです。他に方法はありません(トランザクション、シグナル、準備されたステートメントなどで試しました)。

0
tombom