3.6.5 Поддержка предзаготовленных заявлений CALL
В этом разделе описывается поддержка предзаготовленных заявлений в C API для хранимых процедур, выполняемых с помощью заявлений:
Хранимые процедуры, выполняемые с помощью предзаготовленных заявлений, могут быть использованы следующим образом:
Хранимая процедура может генерировать любое количество наборов результатов. Количество столбцов и типы данных столбцов не должны быть одинаковыми для всех наборов результатов.
-
Конечные значения параметров
OUTиINOUTдоступны вызывающему приложению после возврата процедуры. Эти параметры возвращаются как дополнительный набор результатов с одной строкой после любых наборов результатов, сгенерированных самой процедурой. Строка содержит значения параметровOUTиINOUTв порядке их объявления в списке параметров процедуры.Дополнительную информацию об эффекте необработанных условий на параметры процедуры см. в .
Следующее обсуждение показывает, как использовать эти возможности через C API для предзаготовленных заявлений. Чтобы использовать предзаготовленные заявления через заявления и , см. .
Приложение, выполняющее предзаготовленное заявление, должно использовать цикл, который извлекает результат, а затем вызывает mysql_stmt_next_result(), чтобы определить, есть ли еще результаты. Результаты состоят из любых наборов результатов, сгенерированных хранимой процедурой, за которыми следует конечное значение состояния, указывающее, завершилась ли процедура успешно.
Если процедура имеет параметры OUT или INOUT, набор результатов, предшествующий конечному значению состояния, содержит их значения. Чтобы определить, содержит ли набор результатов значения параметров, проверьте, установлена ли бита SERVER_PS_OUT_PARAMS в члене MYSQL обработчика соединения:
mysql->server_status & SERVER_PS_OUT_PARAMS
Следующий пример использует предзаготовленное заявление для выполнения хранимой процедуры, которая генерирует несколько наборов результатов и возвращает значения параметров вызывающей стороне с помощью параметров OUT и INOUT. Процедура принимает параметры всех трех типов (IN, OUT, INOUT), отображает их начальные значения, присваивает новые значения, отображает обновленные значения и возвращается. Поэтому ожидаемая возвращаемая информация от процедуры состоит из нескольких наборов результатов и конечного состояния:
Один набор результатов от , отображающий начальные значения параметров:
10,NULL,30. (ПараметруOUTприсваивается значение вызывающей стороной, но ожидается, что это присваивание будет неэффективным: параметрыOUTрассматриваются какNULLвнутри процедуры до присвоения значения внутри процедуры.)Один набор результатов от , отображающий измененные значения параметров:
100,200,300.Один набор результатов, содержащий конечные значения параметров
OUTиINOUT:200,300.Пакетик конечного состояния.
Код для выполнения процедуры:
MYSQL_STMT *stmt;
MYSQL_BIND ps_params[3]; /* input parameter buffers */
int int_data[3]; /* input/output values */
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
Код использует две служебные функции, 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);
}
}
© 2025 Oracle
Licensed under the GPLv2 License.