3.6.4 Поддержка подготовленных вызовов хранимых процедур
В данном разделе описывается поддержка подготовленных запросов в C API для хранимых процедур, выполняемых с помощью операторов:
Хранимые процедуры, выполняемые с помощью подготовленных операторов, могут использоваться следующим образом:
Хранимая процедура может генерировать любое количество наборов результатов. Количество столбцов и типы данных столбцов не обязательно должны быть одинаковыми для всех наборов результатов.
-
Конечные значения параметров
OUTиINOUTдоступны вызывающему приложению после возврата процедуры. Эти параметры возвращаются как дополнительный набор результатов из одной строки после любых наборов результатов, сгенерированных самой процедурой. Строка содержит значения параметровOUTиINOUTв порядке их объявления в списке параметров процедуры.Сведения об эффекте необработанных условий на параметры процедуры см. в .
Ниже приводится описание того, как использовать эти возможности через C API для подготовленных операторов. Чтобы использовать подготовленные операторы через операторы и , см. .
Приложение, выполняющее подготовленный оператор, должно использовать цикл, который извлекает результат, а затем вызывает mysql_stmt_next_result(), чтобы определить, есть ли еще результаты. Результаты состоят из наборов результатов, сгенерированных хранимой процедурой, за которыми следует конечное значение состояния, указывающее, завершилась ли процедура успешно.
Если у процедуры есть параметры OUT или INOUT, набор результатов, предшествующий конечному значению состояния, содержит их значения. Чтобы определить, содержит ли набор результатов значения параметров, проверьте, установлен ли бит SERVER_PS_OUT_PARAMS в члене server_status обработчика соединения 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 */
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
Код использует две вспомогательные функции, 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.