3

問題

ストアドプロシージャがあります:

CREATE PROCEDURE `ProblematicProcedure` (IN dbName varchar(50), IN tableId INT)
    MODIFIES SQL DATA
BEGIN
    DROP VIEW IF EXISTS v1;
    DROP VIEW IF EXISTS v2;

    CALL ExecuteSql(CONCAT("CREATE VIEW v1 AS SELECT * FROM ",dbName,".my_table;"));
    CALL ExecuteSql(CONCAT("CREATE VIEW v2 AS SELECT * FROM ",dbName,".table_",tableId,";"));

    ...

コマンドラインまたはNavicatやHeidiSqlなどのクライアントから直接呼び出すと、うまく機能します

CALL ProblematicProcedure("my_schema",1); 

ただし、上記とまったく同じ行を使用してカスタムApacheモジュールから呼び出された場合、最初の呼び出しでクラッシュExecuteSqlします。Apacheモジュールから呼び出されたときに動作させる必要があり、クラッシュする理由を見つけることができませんでした。

ExecuteSql意味

CREATE PROCEDURE ExecuteSql (IN sql_str TEXT)
BEGIN
    SET @query = sql_str;
    PREPARE stm FROM @query;
    EXECUTE stm;
    DEALLOCATE PREPARE stm;
END

私が試したことは?

  • ExecuteSql2つの呼び出しを入れ替えました。
  • インラインExecuteSqlコール。
  • を削除し、ハードコードされた値ExecuteSqlを持つ直接SQLステートメントを使用しました。dbNametableId
  • なしでプロシージャを作成しMODIFIES SQL DATAました。
  • 付与されCREATE VIEWた特権:GRANT ALL ON *.* TO 'myuser'@'%';

注:クラッシュしている場所を見つけるために、行の間に単純な挿入ステートメントを追加しました。ExecuteSqlしたがって、最初の呼び出しで常にクラッシュすると確信しています。

質問

このクラッシュの理由は何でしょうか?

更新:最後に、エラーコードを見つけることができました:

エラー1312:プロシージャは指定されたコンテキストで結果セットを返すことができません

4

1 に答える 1

0

解決

CLIENT_MULTI_STATEMENTS接続時にフラグを使用します。

mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);

なぜそうなのですか?

ストアドプロシージャを呼び出すとは、複数のステートメントを実行することを意味します。したがって、一度に複数のステートメントを実行できることを指定する必要があります。したがって、クライアント側( Apacheモジュール内)でMySql C API関数を使用しているのでCLIENT_MULTI_STATEMENTS、接続時にフラグを指定する必要があります。

mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);

または後で設定します。

mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON);

それらは、 CAPIの複数ステートメント実行の処理ページから学びました。


どのようにデバッグしましたか?

ストアドプロシージャのデバッグはそれほど簡単ではありません。従来のログテーブル方式を使用しましたが、エラーコードの検索について少し積極的に実行しました。

まず、発生したエラーに関するコードとメッセージを保持するために2つの変数を定義しました。

DECLARE E INT  DEFAULT 0;    -- error code
DECLARE M TEXT DEFAULT NULL; -- error message

次に、クライアントエラーとサーバーエラーの両方について考えられるエラーコードとメッセージを定義しました(完全なリストはこちら)。

DECLARE CONTINUE HANDLER FOR 1000 SET E='1000', M="hashchk";
DECLARE CONTINUE HANDLER FOR 1001 SET E='1001', M="isamchk";
...
...
DECLARE CONTINUE HANDLER FOR 1312 SET E='1312', M="PROCEDURE %s can't return a result set in the given context";
...
...
DECLARE CONTINUE HANDLER FOR 1638 SET E='1638', M="Non-ASCII separator arguments are not fully supported";
DECLARE CONTINUE HANDLER FOR 1639 SET E='1639', M="debug sync point wait timed out";
DECLARE CONTINUE HANDLER FOR 1640 SET E='1640', M="debug sync point hit limit reached";
...
...
DECLARE CONTINUE HANDLER FOR 2057 SET E='2057', M="The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again";

そして最後に、ログを重要なポイントに配置します。

IF E> 0 THEN
    CALL WriteLog(CONCAT("Error ", E, ": ", M));
END IF;

WriteLogログテーブルにのみ挿入する別の手順です。このメソッドは私にエラーコード(1312)を与え、それからいくつかのグーグルが機能しました。

于 2012-09-06T13:35:44.257 に答える