SQL文のエラー処理

JavaScriptには、Javaのような例外フレームワークが用意されています。node-oracledbのようにErrorオブジェクトをpromiseまたはコールバックとして戻すのではなく、MLE JavaScriptドライバはエラーのスローを使用します。この概念は、PL/SQL開発者にとって非常になじみ深いものです。

JavaScriptコードでのtry-catch-finallyの使用は、PL/SQL開発者がbegin-exception-endブロックを使用して処理中にエラーを検出する方法に似ています。

例外を再スローする必要がある場合は、JavaScriptのthrow()コマンドを使用します。これにより、catchブロックで処理された後にエラーがスタックをバブルアップします。例7-14に、この概念を示します。

例7-13 JavaScriptファンクションの内部でのSQLエラー処理

CREATE TABLE log_t (
    id NUMBER GENERATED ALWAYS AS IDENTITY
    CONSTRAINT pk_log_t PRIMARY KEY,
    err VARCHAR2(255),
    msg VARCHAR2(255)
);

CREATE OR REPLACE PACKAGE logging_pkg as
  PROCEDURE log_err(p_msg VARCHAR2, p_err VARCHAR2);
END logging_pkg;
/

CREATE OR REPLACE PACKAGE BODY logging_pkg AS
  PROCEDURE log_err(p_msg VARCHAR2, p_err VARCHAR2)
  AS
    PRAGMA autonomous_transaction;
  BEGIN
    INSERT INTO log_t (
        err,
        msg
    ) VALUES (
        p_err,
        p_msg
    );
    COMMIT;
  END log_err;
END logging_pkg;
/

CREATE OR REPLACE MLE MODULE js_err_handle_mod
LANGUAGE JAVASCRIPT AS

/**
 *short demo showing how to use try/catch to catch an error
 *and proceeding normally. In the example, the error is 
 *provoked
*/
export function errorHandlingDemo(){

    try{
        const result = session.execute(
            `INSERT INTO
                surelyThisTableDoesNotExist
            VALUES
                (1)`
        );

    console.log(`there were ${result.rowsAffected} rows inserted`);

    } catch(err) {
        logError('this is some message', err);

        //tell the caller that something went wrong
        return false;
    }

    //further processing

    //return successful completion of the code
    return true;
}

/**
 *log an error using the logging_pkg created at the beginning
 *of this example. Think of it as a package logging errors in 
 *a framework for later analysis.
 *@param msg an accompanying message
 *@param err the error encountered
*/
function logError(msg, err){
    const result = session.execute(
        `BEGIN
            logging_pkg.log_err(
                p_msg => :msg,
                p_err => :err
            );
        END;`,
        {
            msg: {
                val: msg,
                dir: oracledb.BIND_IN
            },
            err: {
                val: err.message,
                dir: oracledb.BIND_IN
            }
        }
    );
}
/

次のようにモジュールjs_err_handle_modを使用して、ファンクションjs_err_handle_mod_fを作成します:

CREATE OR REPLACE FUNCTION js_err_handle_mod_f
RETURN BOOLEAN
AS MLE MODULE js_err_handle_mod
SIGNATURE 'errorHandlingDemo()';
/

これで、ファンクションをコールし、戻り値を使用して、処理が成功したかどうかを確認できます:

DECLARE
    l_success boolean := false;
BEGIN
    l_success := js_err_handle_mod_f;

    IF l_success THEN
        DBMS_OUTPUT.PUT_LINE('normal, successful completion');
    ELSE
        DBMS_OUTPUT.PUT_LINE('an error has occurred');
    END IF;
END;
/

この場合、エラーはMLEモジュール内で捕捉されます。エラーはアプリケーションによって記録されるため、管理者は状況を評価して修正アクションを実行できます。

例7-14 JavaScriptのthrow()コマンドを使用したエラー処理

この例は、catchブロックにおけるJavaScriptのthrow()コマンドの使用を示しています。例7-13js_err_handle_modについて表示された画面出力とは異なり、コール側のPL/SQLブロックがエラーを捕捉し、それを適宜処理するか、再度生成する必要があります。

CREATE OR REPLACE MLE MODULE js_throw_mod
LANGUAGE JAVASCRIPT AS

/**
 *a similar example as Example 7-13, however, rather than
 *processing the error in the JavaScript code, it is re-thrown up the call stack.
 *It is now up to the called to handle the exception. The try/catch block is not 
 *strictly necessary but is used in this example as a cleanup step to remove Global
 *Temporary Tables (GTTs) and other temporary objects that are no longer required.
*/
export function rethrowError(){
    
    try{
        const result = session.execute(
            `INSERT INTO 
                surelyThisTableDoesNotExist
            VALUES
                (1)`
        );

        console.log(`there were ${result.rowsAffected} rows inserted`);

    } catch(err){
        cleanUpBatch();

        throw(err);
    }

    //further processing
}

function cleanUpBatch(){
    //batch cleanup operations
    return;
}
/

次のコール仕様を使用すると、エラーの捕捉に失敗すると予期しないエラーが発生し、エンド・ユーザーまでコール・スタックを伝播できます。

CREATE OR REPLACE PROCEDURE rethrow_err_proc
AS MLE MODULE js_throw_mod
SIGNATURE 'rethrowError()';
/

BEGIN
    rethrow_err_proc;
END;
/

結果:

BEGIN
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-04171: at rethrowError (USER1.JS_THROW_MOD:11:24)
ORA-06512: at "USER1.RETHROW_ERROR_PROC", line 1
ORA-06512: at line 2

エンド・ユーザーに、このタイプのエラーが表示されないようにします。かわりに、よりわかりやすいメッセージを表示します。この例を続けて、単純な修正として例外ブロックを追加します:

BEGIN
    rethrow_err_proc;
EXCEPTION
    WHEN OTHERS THEN
        logging_pkg.log_err(
            'something went wrong',
            sqlerrm
        );
        --this would be shown on the user interface;
        --for the sake of demonstration this workaround
        --is used to show the concept
        DBMS_OUTPUT.PUT_LINE(
            'ERROR: the process encountered an unexpected error'
        );
        DBMS_OUTPUT.PUT_LINE(
            'please inform the administrator referring to application error 1234'
        );
END;
/

結果:

ERROR: the process encountered an unexpected error
please inform the administrator referring to application error 1234

PL/SQL procedure successfully completed.