このセクションでは、CALL
ステートメントを使用して実行されるストアドプロシージャーに対する C API のプリペアドステートメントのサポートについて説明します。
MySQL 5.6 では、プリペアド CALL
ステートメントを使用して実行されるストアドプロシージャーを次のように使用できます。
ストアドプロシージャーは任意の数の結果セットを生成できます。カラムの数とカラムのデータ型はすべての結果セットで同じにする必要はありません。
OUT
およびINOUT
パラメータの最終値は、プロシージャーが戻ったあとに、呼び出し元のアプリケーションで使用できます。これらのパラメータは、プロシージャー自体によって生成される結果セットに続いて、特別な単一行結果セットとして返されます。行には、OUT
およびINOUT
パラメータがプロシージャーパラメータリストで宣言されている順番で含まれます。
次の説明に、プリペアドステートメントに対して、C API からこれらの機能を使用する方法を示します。PREPARE
および EXECUTE
ステートメントからプリペアド CALL
ステートメントを使用するには、セクション13.2.1「CALL 構文」を参照してください。
アプリケーションが 5.5.3 より古い MySQL のバージョンが使用されたコンテキストでコンパイルまたは実行されている可能性がある場合、複数の結果セットのプリペアド CALL
機能および OUT
または INOUT
パラメータを使用できない可能性があります。
クライアント側で、ライブラリが MySQL 5.5.3 以上のものでないと、アプリケーションはコンパイルされません (そのバージョンで導入された API 関数およびシンボルが存在しなくなります)。
-
サーバーが十分に新しいことを実行時に確認するには、クライアントでこのテストを使用できます。
if (mysql_get_server_version(mysql) < 50503) { fprintf(stderr, "Server does not support required CALL capabilities\n"); mysql_close(mysql); exit (1); }
プリペアド CALL
ステートメントを実行するアプリケーションは、結果をフェッチし、次に mysql_stmt_next_result()
を呼び出して、それ以上の結果があるかどうかを判断するループを使用してください。結果は、ストアドプロシージャーによって生成された結果セットと、それに続くプロシージャーが正常に終了したかどうかを示す最終ステータス値から構成されます。
プロシージャーに OUT
または INOUT
パラメータがある場合、最終ステータス値の前の結果セットにそれらの値が格納されます。結果セットにパラメータ値が格納されているかどうかを判断するには、MYSQL
接続ハンドラの server_status
メンバーに、SERVER_PS_OUT_PARAMS
ビットが設定されているかどうかをテストします。
mysql->server_status & SERVER_PS_OUT_PARAMS
次の例では、プリペアド CALL
ステートメントを使用して、複数の結果セットを生成して、OUT
および INOUT
パラメータを使用して、呼び出し元にパラメータ値を返すストアドプロシージャーを実行しています。プロシージャーは 3 つすべての型 (IN
、OUT
、INOUT
) のパラメータをとり、それらの初期値を表示して、新しい値を割り当て、更新された値を表示して戻ります。そのため、プロシージャーからの予期される戻り情報は、複数の結果セットと最終ステータスから構成されます。
初期パラメータ値 (
10
、NULL
、30
) を表示するSELECT
からの 1 つの結果セット。(OUT
パラメータには呼び出し元によって値が割り当てられますが、この割り当ては無効にすることが期待されます。OUT
パラメータは、プロシージャー内で値を割り当てるまで、プロシージャー内でNULL
とみなされます。)変更されたパラメータ値 (
100
、200
、300
) を表示するSELECT
からの 1 つの結果セット。最終
OUT
およびINOUT
パラメータ値 (200
、300
) を格納する 1 つの結果セット。最終ステータスパケット。
プロシージャーを実行するコード:
MYSQL_STMT *stmt;
MYSQL_BIND ps_params[3]; /* input parameter buffers */
int int_data[3]; /* input/output values */
my_bool is_null[3]; /* output value nullability */
int status;
/* set up stored procedure */
status = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
test_error(mysql, status);
status = mysql_query(mysql,
"CREATE PROCEDURE p1("
" IN p_in INT, "
" OUT p_out INT, "
" INOUT p_inout INT) "
"BEGIN "
" SELECT p_in, p_out, p_inout; "
" SET p_in = 100, p_out = 200, p_inout = 300; "
" SELECT p_in, p_out, p_inout; "
"END");
test_error(mysql, status);
/* initialize and prepare CALL statement with parameter placeholders */
stmt = mysql_stmt_init(mysql);
if (!stmt)
{
printf("Could not initialize statement\n");
exit(1);
}
status = mysql_stmt_prepare(stmt, "CALL p1(?, ?, ?)", 16);
test_stmt_error(stmt, status);
/* initialize parameters: p_in, p_out, p_inout (all INT) */
memset(ps_params, 0, sizeof (ps_params));
ps_params[0].buffer_type = MYSQL_TYPE_LONG;
ps_params[0].buffer = (char *) &int_data[0];
ps_params[0].length = 0;
ps_params[0].is_null = 0;
ps_params[1].buffer_type = MYSQL_TYPE_LONG;
ps_params[1].buffer = (char *) &int_data[1];
ps_params[1].length = 0;
ps_params[1].is_null = 0;
ps_params[2].buffer_type = MYSQL_TYPE_LONG;
ps_params[2].buffer = (char *) &int_data[2];
ps_params[2].length = 0;
ps_params[2].is_null = 0;
/* bind parameters */
status = mysql_stmt_bind_param(stmt, ps_params);
test_stmt_error(stmt, status);
/* assign values to parameters and execute statement */
int_data[0]= 10; /* p_in */
int_data[1]= 20; /* p_out */
int_data[2]= 30; /* p_inout */
status = mysql_stmt_execute(stmt);
test_stmt_error(stmt, status);
/* process results until there are no more */
do {
int i;
int num_fields; /* number of columns in result */
MYSQL_FIELD *fields; /* for result set metadata */
MYSQL_BIND *rs_bind; /* for output buffers */
/* the column count is > 0 if there is a result set */
/* 0 if the result is only the final status packet */
num_fields = mysql_stmt_field_count(stmt);
if (num_fields > 0)
{
/* there is a result set to fetch */
printf("Number of columns in result: %d\n", (int) num_fields);
/* what kind of result set is this? */
printf("Data: ");
if(mysql->server_status & SERVER_PS_OUT_PARAMS)
printf("this result set contains OUT/INOUT parameters\n");
else
printf("this result set is produced by the procedure\n");
MYSQL_RES *rs_metadata = mysql_stmt_result_metadata(stmt);
test_stmt_error(stmt, rs_metadata == NULL);
fields = mysql_fetch_fields(rs_metadata);
rs_bind = (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields);
if (!rs_bind)
{
printf("Cannot allocate output buffers\n");
exit(1);
}
memset(rs_bind, 0, sizeof (MYSQL_BIND) * num_fields);
/* set up and bind result set output buffers */
for (i = 0; i < num_fields; ++i)
{
rs_bind[i].buffer_type = fields[i].type;
rs_bind[i].is_null = &is_null[i];
switch (fields[i].type)
{
case MYSQL_TYPE_LONG:
rs_bind[i].buffer = (char *) &(int_data[i]);
rs_bind[i].buffer_length = sizeof (int_data);
break;
default:
fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type);
exit(1);
}
}
status = mysql_stmt_bind_result(stmt, rs_bind);
test_stmt_error(stmt, status);
/* fetch and display result set rows */
while (1)
{
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA)
break;
for (i = 0; i < num_fields; ++i)
{
switch (rs_bind[i].buffer_type)
{
case MYSQL_TYPE_LONG:
if (*rs_bind[i].is_null)
printf(" val[%d] = NULL;", i);
else
printf(" val[%d] = %ld;",
i, (long) *((int *) rs_bind[i].buffer));
break;
default:
printf(" unexpected type (%d)\n",
rs_bind[i].buffer_type);
}
}
printf("\n");
}
mysql_free_result(rs_metadata); /* free metadata */
free(rs_bind); /* free output buffers */
}
else
{
/* no columns = final status packet */
printf("End of procedure output\n");
}
/* more results? -1 = no, >0 = error, 0 = yes (keep looking) */
status = mysql_stmt_next_result(stmt);
if (status > 0)
test_stmt_error(stmt, status);
} while (status == 0);
mysql_stmt_close(stmt);
プロシージャーを実行すると次の出力が生成されます。
Number of columns in result: 3
Data: this result set is produced by the procedure
val[0] = 10; val[1] = NULL; val[2] = 30;
Number of columns in result: 3
Data: this result set is produced by the procedure
val[0] = 100; val[1] = 200; val[2] = 300;
Number of columns in result: 2
Data: this result set contains OUT/INOUT parameters
val[0] = 200; val[1] = 300;
End of procedure output
このコードでは、2 つのユーティリティールーチン test_error()
および test_stmt_error()
を使用して、エラーをチェックし、エラーが発生した場合は診断情報を出力したあとに終了します。
static void test_error(MYSQL *mysql, int status)
{
if (status)
{
fprintf(stderr, "Error: %s (errno: %d)\n",
mysql_error(mysql), mysql_errno(mysql));
exit(1);
}
}
static void test_stmt_error(MYSQL_STMT *stmt, int status)
{
if (status)
{
fprintf(stderr, "Error: %s (errno: %d)\n",
mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
exit(1);
}
}