9

準備済みステートメントでストアド関数を使用しようとしていますが、成功していません。

MySQL はステートメントを準備しますが、それを実行せず、エラーは発生せず、黙って失敗します。

これは*関数です:


drop function if exists lineDistanceC;
delimiter //
CREATE FUNCTION lineDistanceC (la1 DOUBLE, lo1 DOUBLE, la2 DOUBLE, lo2 DOUBLE) RETURNS DOUBLE 
BEGIN 
SET @r = 6371;
SET @lat1 = RADIANS(la1);
SET @lon1 = RADIANS(lo1);
SET @lat2 = RADIANS(la2);
SET @lon2 = RADIANS(lo2);
SET @x = (@lon2-@lon1) * COS((@lat1+@lat2)/2);
SET @y = (@lat2 - @lat1);
RETURN (SQRT((@x*@x) + (@y*@y)) * @r);
END
//
delimiter ;

コマンドラインで期待どおりに動作し、SELECT ステートメントでハードコードされた double を置き換えると、期待どおりに動作するようになるため、周囲のコードも問題ありません。

私は CLIENT_MULTI_STATEMENTS を MySQL 接続パラメーターとして有効にしました。これは CALLing プロシージャーにのみ関連していると思われますが、それをつかむ価値のあるストローでした。

ステートメントは、次を使用して準備されます。


strcpy (sqlStr, "SELECT lineDistanceC(Y(pnt),X(pnt), ?, ?) FROM mytable");
MYSQL_STMT *statement;
if (mysql_stmt_prepare(statement, sqlStr, strlen(sqlStr))) {
    fprintf(stderr, "ERROR:mysql_stmt_prepare() failed. Error:%s\nsql:%s\n", mysql_stmt_error(statement), sqlStr);
}

パラメータと結果に必要な bind ステートメントが続き、次を使用して実行されます。


if (mysql_stmt_execute(statement)) {
    fprintf(stderr, "mysql_stmt_execute(), failed. Error:%s\n", mysql_stmt_error(statement));
}

前述のように、上記の SELECT ステートメントの関数がハード コードの浮動小数点数 (たとえば、SELECT 2.3 .....) に置き換えられた場合、期待どおりにハード コードされた数値が返されます。

実際には、準備されたステートメントコードはマクロを使用して挿入されます。必要に応じて機能する何百ものステートメントがあるため、準備、バインディング、または実行構文は問題ではありません (上記が正しくない場合、間違っているのはマクロからの私の翻訳です) ) 関数で使用するために異なる必要がある場合を除きます。

これらは、入力の 1 つが出力としてバインドされている場合のログ エントリです。


Prepare SELECT ? FROM mytable
Execute SELECT 51.36000061035156 FROM mytable

これは、関数を使用しようとしたときのログ エントリです。


Prepare SELECT lineDistanceC(latitude, longitude, ?, ?) FROM mytable;

前述のように、ステートメントを実行せずにサイレントに失敗し、ログやその他の場所にエラーはありません。

手がかりをいただければ幸いです。

*当然のことながら、この関数はここの式から導出されました

4

1 に答える 1

4

テストする簡単な例をハックして、MySql5.1.66で問題を再現できませんでした。ログにPrepareステートメントが表示されている場合は、クエリが構文的に正しく、サーバーがパラメーターを受け入れる準備ができていることを示しています。入力パラメータを正しくバインドしないと、Queryログメッセージは表示されません。

バインドマクロが正しいCコードを生成していることを再度確認する必要があります。次のスニペットは、デバッグに役立ついくつかのアイデアを提供する可能性のあるAPI呼び出しの機能セットを示しています。システムでのバイナリロギングのエラーを回避するためにDETERMINISTIC句を追加するために、関数に加えた唯一の変更。

次のようなテーブルから始めます。

mysql> select * from mytable;
+----+-------+
| id | value |
+----+-------+
|  1 |     1 |
|  2 |     2 |
|  3 |     3 |
+----+-------+
3 rows in set (0.00 sec)

次に、次のクエリを実行すると、次の結果が得られます。

mysql> SELECT lineDistanceC(1.0, 2.0, 3.0, value) FROM mytable;
+-------------------------------------+
| lineDistanceC(1.0, 2.0, 3.0, value) |
+-------------------------------------+
|                    248.609129229989 |
|                    222.389853289118 |
|                    248.609129229989 |
+-------------------------------------+
3 rows in set (0.00 sec)

次のCコードは同じクエリを試行しますが、MySqlAPIを使用します。メモリ管理と正しいエラーチェックは実装されていないことに注意してください。

/* mysql_test.c */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <mysql.h>


int main(int argc, char * argv[])
{
    char sqlStr[300];


    MYSQL *mysql = mysql_init(NULL);
    if (mysql == NULL) {
        fprintf(stderr, "ERROR:mysql_init() failed.\n");
        exit(1);
    }

    const char* host = "localhost";
    const char* user = "root";
    const char* passwd = NULL;
    const char* db = "mysql_test";

    if (mysql_real_connect(mysql, host, user, passwd, db, 0, NULL, 0) == NULL) {
        fprintf(stderr, "ERROR:mysql_real_connect() failed.\n");
        exit(1);
    }


    MYSQL_STMT *statement = NULL;
    statement = mysql_stmt_init(mysql);
    if (statement == NULL) {
        fprintf(stderr, "ERROR:mysql_stmt_init() failed.\n");
        exit(1);
    }

    strcpy (sqlStr, "SELECT lineDistanceC(1.0, 2.0, ?, value) FROM mytable");

    if (mysql_stmt_prepare(statement, sqlStr, strlen(sqlStr))) {
        fprintf(stderr, "ERROR:mysql_stmt_prepare() failed. Error:%s\nsql:%s\n", mysql_stmt_error(statement), sqlStr);
        exit(1);
    }

    MYSQL_BIND input_bind[1];
    memset(input_bind, 0, sizeof(input_bind));
    float v1 = 3.0;
    unsigned long v1_len = sizeof(v1);

    input_bind[0].buffer_type = MYSQL_TYPE_FLOAT;
    input_bind[0].buffer = &v1;
    input_bind[0].buffer_length = sizeof(v1);
    input_bind[0].length = &v1_len;
    input_bind[0].is_null = (my_bool*)0;

    if (mysql_stmt_bind_param(statement, input_bind)) {
        fprintf(stderr, "ERROR:mysql_stmt_bind_param failed\n");
        exit(1);
    }

    if (mysql_stmt_execute(statement)) {
        fprintf(stderr, "mysql_stmt_execute(), failed. Error:%s\n", mysql_stmt_error(statement));
        exit(1);
    }

    /* Fetch result set meta information */
    MYSQL_RES* prepare_meta_result = mysql_stmt_result_metadata(statement);
    if (!prepare_meta_result)
    {
        fprintf(stderr,
                " mysql_stmt_result_metadata(), \
                returned no meta information\n");
        fprintf(stderr, " %s\n", mysql_stmt_error(statement));
        exit(1);
    }

    /* Get total columns in the query */
    int column_count= mysql_num_fields(prepare_meta_result);
    if (column_count != 1) /* validate column count */
    {
        fprintf(stderr, " invalid column count returned by MySQL\n");
        exit(1);
    }

    /* Bind single result column, expected to be a double. */
    MYSQL_BIND result_bind[1];
    memset(result_bind, 0, sizeof(result_bind));

    my_bool result_is_null[1];
    double result_double;
    unsigned long result_len = 0;

    result_bind[0].buffer_type = MYSQL_TYPE_DOUBLE;
    result_bind[0].buffer = &result_double;
    result_bind[0].buffer_length = sizeof(result_double);
    result_bind[0].length = &result_len;
    result_bind[0].is_null = &result_is_null[1];

    if (mysql_stmt_bind_result(statement, result_bind)) {
        fprintf(stderr, "mysql_stmt_bind_Result(), failed. Error:%s\n", mysql_stmt_error(statement));
        exit(1);
    }

    while (!mysql_stmt_fetch(statement)) {
        printf("%.12f\n", result_double);
    }

}

コンパイル:

gcc -o mysql_test mysql_test.c -lmysqlclient

出力は次のとおりです。

248.609129229989
222.389853289118
248.609129229989

これは、mysqlコマンドラインクライアントを使用して実行されたステートメントの出力と一致します。

これをコンパイルしてシステムで実行し、問題がAPIの使用にあるのか他の問題にあるのかを判断できるはずです。

于 2013-03-18T00:07:25.313 に答える