【Oracle】ORA-01003の原因と解決方法|no statement parsed・SQLがパースされていない時の直し方

【Oracle】ORA-01003の原因と解決方法|no statement parsed・SQLがパースされていない時の直し方 Oracle

ORA-01003: no statement parsed は、Oracleでカーソルを使った処理を実行・フェッチ・バインドしようとした時に、そのカーソルへパース済みのSQL文が関連付いていない場合に発生するエラーです。特に DBMS_SQL、OCI系のアプリ、古いForms/Reports、動的SQLをラップした処理で見かけます。

Oracle公式の説明では、ホスト言語プログラムの呼び出しが、関連付けられたパース済みSQL文を持たないカーソルを参照したことが原因です。対処は、データをフェッチする前にSQL文をパースして実行することです。

先に結論
ORA-01003が出たら、カーソルに対して PARSE が成功しているか、その後に EXECUTEFETCH しているかを確認します。空SQL、パース失敗後の続行、クローズ済みカーソルの再利用、別カーソル変数の取り違えが定番原因です。
スポンサーリンク

ORA-01003とは

Oracleでカーソルを低レベルに扱う処理では、一般に「カーソルを開く」「SQLをパースする」「バインドする」「実行する」「フェッチする」という順番があります。ORA-01003は、このうちSQLをパースしてカーソルへ関連付ける段階が抜けている、または失敗している時に起きます。

確認点 よくある原因 見る場所
パース済みか DBMS_SQL.PARSE が呼ばれていない DBMS_SQLの呼び出し順
パース成功後か 例外を握りつぶして後続処理を続けている 例外処理、ログ
SQL文字列 空文字、NULL、不完全なSQLを渡している 実行直前のSQLログ
カーソル状態 クローズ後や別カーソルを操作している OPEN/CLOSEの位置
アプリ側 prepare前にexecute/fetch相当の処理をしている JDBC/OCI/ラッパー

DBMS_SQL の基本は DBMS_SQL完全ガイド、単純な動的SQLは EXECUTE IMMEDIATEガイド も参考になります。

ORA-01001・ORA-01002との違い

ORA-01003はカーソル系のエラーですが、似たものに ORA-01001ORA-01002 があります。切り分けでは、カーソルが無効なのか、フェッチ順が不正なのか、そもそもSQLがパースされていないのかを分けます。

ORA-01003

主な意味: カーソルにパース済みSQL文が関連付いていない

見る場所: PARSE、EXECUTE、FETCHの順番

ORA-01001

主な意味: 無効なカーソルを操作している

関連記事: ORA-01001

ORA-01002

主な意味: フェッチ順やカーソル状態が不正

関連記事: ORA-01002

DBMS_SQL.PARSE前にEXECUTEしている

DBMS_SQL では、開いたカーソルにSQL文を PARSE してから EXECUTE します。カーソルを開いただけで実行すると、カーソルにSQLが関連付いていないためORA-01003の原因になります。

dbms-sql-execute-before-parse.sql
DECLARE
  c INTEGER;
  n INTEGER;
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;

  -- NG: PARSEしていないカーソルを実行している
  n := DBMS_SQL.EXECUTE(c);

  DBMS_SQL.CLOSE_CURSOR(c);
END;
/
dbms-sql-parse-before-execute.sql
DECLARE
  c INTEGER;
  n INTEGER;
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;

  DBMS_SQL.PARSE(
    c,
    'UPDATE employees SET updated_at = SYSDATE WHERE employee_id = :id',
    DBMS_SQL.NATIVE
  );
  DBMS_SQL.BIND_VARIABLE(c, ':id', 100);
  n := DBMS_SQL.EXECUTE(c);

  DBMS_SQL.CLOSE_CURSOR(c);
END;
/

SELECTをPARSEしただけでFETCHしている

SELECT文では、PARSE だけでなく EXECUTE、必要に応じて DEFINE_COLUMNFETCH_ROWSCOLUMN_VALUE の順番も確認します。処理順が崩れると、ORA-01003や周辺のカーソルエラーにつながります。

dbms-sql-select-correct-order.sql
DECLARE
  c      INTEGER;
  n      INTEGER;
  v_name VARCHAR2(100);
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(c,
    'SELECT employee_name FROM employees WHERE employee_id = :id',
    DBMS_SQL.NATIVE);
  DBMS_SQL.BIND_VARIABLE(c, ':id', 100);
  DBMS_SQL.DEFINE_COLUMN(c, 1, v_name, 100);

  n := DBMS_SQL.EXECUTE(c);
  IF DBMS_SQL.FETCH_ROWS(c) > 0 THEN
    DBMS_SQL.COLUMN_VALUE(c, 1, v_name);
  END IF;

  DBMS_SQL.CLOSE_CURSOR(c);
END;
/

パース失敗後に処理を続けている

SQL文の構文エラー、存在しない表や列、権限不足などで PARSE が失敗したのに、例外を握りつぶして EXECUTEFETCH を続けると、後続でORA-01003として見えることがあります。本当の原因はORA-009xxや権限エラーの方にある場合があります。

parse-error-then-continue.sql
DECLARE
  c INTEGER;
  n INTEGER;
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;
  BEGIN
    -- NG: SQLが不完全でPARSEに失敗する
    DBMS_SQL.PARSE(c, 'SELECT FROM employees', DBMS_SQL.NATIVE);
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM);
      -- ここで続行すると原因が分かりにくくなる
  END;

  n := DBMS_SQL.EXECUTE(c);
  DBMS_SQL.CLOSE_CURSOR(c);
END;
/

PARSE が失敗したら、そのSQL文字列、エラー番号、バインド一覧をログに出して処理を止めます。SQL構文の切り分けでは ORA-00911、バインド不足なら ORA-01008、SQLにないバインドを渡しているなら ORA-01006 も確認します。

動的SQLの調査では、DBMS_SQL.LAST_ERROR_POSITION でパースエラー位置を残すと原因箇所を追いやすくなります。エラースタックに ORA-06512 が出ている場合は、ORA-06512の読み方 で呼び出し元の行番号も確認します。

safe-parse-with-cleanup.sql
DECLARE
  c     INTEGER;
  v_sql VARCHAR2(4000) := 'SELECT FROM employees';
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;

  BEGIN
    DBMS_SQL.PARSE(c, v_sql, DBMS_SQL.NATIVE);
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('SQL=' || v_sql);
      DBMS_OUTPUT.PUT_LINE('ERR=' || SQLERRM);
      DBMS_OUTPUT.PUT_LINE('POS=' || DBMS_SQL.LAST_ERROR_POSITION);

      IF DBMS_SQL.IS_OPEN(c) THEN
        DBMS_SQL.CLOSE_CURSOR(c);
      END IF;

      RAISE;
  END;

  DBMS_SQL.CLOSE_CURSOR(c);
END;
/

SQL文字列がNULLまたは空になっている

動的SQLを条件分岐で作っていると、実行直前のSQL文字列が NULL や空文字、不完全な文になっていることがあります。ソース上では代入しているつもりでも、分岐に入らず空のままになるケースです。

empty-dynamic-sql-guard.sql
DECLARE
  v_sql VARCHAR2(4000);
BEGIN
  -- 条件によってv_sqlに何も入らないことがある

  IF v_sql IS NULL OR TRIM(v_sql) IS NULL THEN
    RAISE_APPLICATION_ERROR(-20001, 'SQL text is empty');
  END IF;

  EXECUTE IMMEDIATE v_sql;
END;
/

エラー時には、SQL文字列そのものをログへ出します。バインド値だけをログに出しても、カーソルへどのSQLをパースしたのか分からないため、原因を追いにくくなります。

カーソルの取り違え・クローズ後操作

複数のカーソル変数を扱う処理では、パースしたカーソルと実行するカーソルが違っていないか確認します。また、クローズ済みカーソルを再利用すると、ORA-01001 など別のカーソルエラーとして出ることもあります。

wrong-cursor-handle.sql
DECLARE
  c_parsed INTEGER;
  c_exec   INTEGER;
  n        INTEGER;
BEGIN
  c_parsed := DBMS_SQL.OPEN_CURSOR;
  c_exec   := DBMS_SQL.OPEN_CURSOR;

  DBMS_SQL.PARSE(c_parsed, 'DELETE FROM work_table', DBMS_SQL.NATIVE);

  -- NG: PARSEしたc_parsedではなく、別カーソルc_execを実行している
  n := DBMS_SQL.EXECUTE(c_exec);

  DBMS_SQL.CLOSE_CURSOR(c_exec);
  DBMS_SQL.CLOSE_CURSOR(c_parsed);
END;
/

JDBCやアプリ側で起きる場合

JDBCなど通常の高レベルAPIでは、明示的にPARSEを呼ぶことは少ないです。それでも、内部でStatementを再利用しているラッパー、SQLが空のまま渡される処理、prepare失敗後のexecute継続で似た状況になります。

java-empty-sql-guard.java
String sql = buildSql(request);

if (sql == null || sql.isBlank()) {
    throw new IllegalArgumentException("SQL text is empty");
}

try (PreparedStatement ps = conn.prepareStatement(sql)) {
    bindParameters(ps, request);
    ps.executeQuery();
}

アプリでは、prepare/parseに相当する処理で失敗した例外を握りつぶさないことが大切です。列数の取り違えなら ORA-01007、バインド名や番号なら ORA-01036 も確認します。

調査手順

ORA-01003は、最初に失敗した場所が別にあることも多いです。後続のORA-01003だけを見るのではなく、直前のパースエラーやSQLログを確認します。

順番 確認すること 見るポイント
1 実行直前のSQL文字列を確認する NULL、空、不完全なSQLではないか
2 PARSEが成功しているか確認する 例外を握りつぶしていないか
3 PARSEしたカーソルを実行しているか確認する 別カーソル変数を使っていないか
4 EXECUTE/FETCHの順番を見る PARSE、BIND、DEFINE、EXECUTE、FETCHの順番
5 周辺エラーと分ける ORA-01001、ORA-01002、ORA-01006、ORA-01036

チェックリスト

  • DBMS_SQL.PARSEEXECUTE より前に呼んでいる
  • PARSE で例外が出たら後続処理を止めている
  • パース失敗時に DBMS_SQL.LAST_ERROR_POSITION とSQL文字列をログに残している
  • 例外時も DBMS_SQL.IS_OPEN を確認してカーソルを閉じている
  • 実行直前のSQL文字列がNULLや空ではない
  • パースしたカーソルと実行するカーソルが同じ
  • SELECTでは DEFINE_COLUMNEXECUTEFETCH_ROWS の順番を確認した
  • クローズ済みカーソルを再利用していない
  • アプリ側でprepare失敗後にexecuteを続けていない

よくある質問

ORA-01003はSQL構文エラーですか?

直接の意味は、カーソルにパース済みSQLが関連付いていないことです。ただし、その前段でSQL構文エラーが起きて PARSE に失敗しているケースはあります。

EXECUTE IMMEDIATEでもORA-01003になりますか?

通常の EXECUTE IMMEDIATE では低レベルのPARSE順序を意識しません。ただしSQL文字列が空、不完全、例外処理で失敗を隠している場合は、周辺エラーとして確認が必要です。

ORA-01001との違いは何ですか?

ORA-01001は無効なカーソルを操作している問題です。ORA-01003は、カーソル自体は参照しているものの、そこにパース済みSQLが関連付いていない問題として切り分けます。

まとめ

ORA-01003は、カーソルにパース済みSQL文が関連付いていない時に発生します。DBMS_SQL.PARSE が成功しているか、パースしたカーソルを実行しているか、SQL文字列が空でないかを確認しましょう。

後続でORA-01003が出ていても、本当の原因は直前のパース失敗にあることがあります。SQL文字列、バインド値、例外ログ、カーソルのOPEN/CLOSEをセットで確認するのが近道です。

参考

ORA-01003 – Oracle Database Error Help

DBMS_SQL – Oracle Database PL/SQL Packages and Types Reference

PL/SQL Dynamic SQL – Oracle Database PL/SQL Language Reference