27.7.15 C API 多语句执行支持

默认情况下,mysql_query()mysql_real_query()将它们的语句字符串参数解释为要执行的单个语句,然后根据该语句产生的是结果集(对于SELECT是一组行)还是受影响的行数( INSERTUPDATE等)。

MySQL 还支持执行包含以分号(;)字符分隔的多个语句的字符串。通过使用mysql_real_connect()连接到服务器或通过调用mysql_set_server_option()连接后指定的特殊选项可以启用此功能。

执行多语句字符串可以产生多个结果集或行计数指示符。处理这些结果所涉及的方法与单语句情况所采用的方法不同:处理完第一条语句的结果后,有必要检查是否存在更多结果,并依次进行处理。为了支持多结果处理,C API 包括mysql_more_results()mysql_next_result()函数。只要有更多结果可用,这些函数就会在循环的结尾使用。 *无法以这种方式处理结果可能会导致与服务器的连接断开。

如果对存储过程执行CALL语句,则还需要多结果处理。存储过程的结果具有以下 Feature:

  • 该过程中的语句可能会产生结果集(例如,如果它执行SELECT语句)。这些结果集按过程执行时的生成 Sequences 返回。

通常,调用者无法知道过程将返回多少结果集。过程执行可能取决于导致执行路径从一个调用到另一个调用的不同的循环或条件语句。因此,您必须准备检索多个结果。

  • 该过程的最终结果是不包含结果集的状态结果。状态指示过程是否成功或发生错误。

多重语句和结果功能只能与mysql_query()mysql_real_query()一起使用。它们不能与 prepared statement 接口一起使用。sched 义的语句处理程序被定义为仅与包含单个语句的字符串一起使用。参见第 27.7.7 节“ C API 准备语句”

为了启用多语句执行和结果处理,可以使用以下选项:

  • mysql_real_connect()函数具有一个flags参数,该参数与两个选项值相关:

  • CLIENT_MULTI_RESULTS使 Client 端程序可以处理多个结果。如果您对产生结果集的存储过程执行CALL语句,则必须启用此选项。否则,此类过程将导致错误Error 1312 (0A000): PROCEDURE proc_name can't return a result set in the given context。在 MySQL 5.7 中,默认情况下启用CLIENT_MULTI_RESULTS

    • CLIENT_MULTI_STATEMENTS使mysql_query()mysql_real_query()能够执行包含由分号分隔的多个语句的语句字符串。此选项还隐式启用CLIENT_MULTI_RESULTS,因此CLIENT_MULTI_STATEMENTSmysql_real_connect()flags自变量等效于CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS的自变量。也就是说,CLIENT_MULTI_STATEMENTS足以启用多语句执行和所有多结果处理。
  • 构建与服务器的连接后,可以通过向mysql_set_server_option()函数传递参数MYSQL_OPTION_MULTI_STATEMENTS_ONMYSQL_OPTION_MULTI_STATEMENTS_OFF来启用或禁用多语句执行。使用此功能启用多语句执行还可以处理多语句字符串的“简单”结果,其中每个语句产生单个结果,但*还不足以允许处理产生结果集的存储过程。

以下过程概述了用于处理多个语句的建议策略:

  • CLIENT_MULTI_STATEMENTS传递给mysql_real_connect(),以完全启用多语句执行和多结果处理。

  • 调用mysql_query()mysql_real_query()并验证是否成功后,进入一个循环,在其中处理语句结果。

  • 对于循环的每次迭代,请处理当前语句的结果,以获取结果集或受影响的行数。如果发生错误,请退出循环。

  • 在循环结束时,调用mysql_next_result()来检查是否存在另一个结果,如果存在则启动检索。如果没有更多结果可用,请退出循环。

下面显示了上述策略的一种可能的实现方式。循环的最后一部分可以简化为mysql_next_result()是否返回非零的简单测试。所编写的代码在没有更多结果和一个错误之间进行区分,该错误使出现后一种情况时可以打印一条消息。

/* connect to server with the CLIENT_MULTI_STATEMENTS option */
if (mysql_real_connect (mysql, host_name, user_name, password,
    db_name, port_num, socket_name, CLIENT_MULTI_STATEMENTS) == NULL)
{
  printf("mysql_real_connect() failed\n");
  mysql_close(mysql);
  exit(1);
}

/* execute multiple statements */
status = mysql_query(mysql,
                     "DROP TABLE IF EXISTS test_table;\
                      CREATE TABLE test_table(id INT);\
                      INSERT INTO test_table VALUES(10);\
                      UPDATE test_table SET id=20 WHERE id=10;\
                      SELECT * FROM test_table;\
                      DROP TABLE test_table");
if (status)
{
  printf("Could not execute statement(s)");
  mysql_close(mysql);
  exit(0);
}

/* process each statement result */
do {
  /* did current statement return data? */
  result = mysql_store_result(mysql);
  if (result)
  {
    /* yes; process rows and free the result set */
    process_result_set(mysql, result);
    mysql_free_result(result);
  }
  else          /* no result set or error */
  {
    if (mysql_field_count(mysql) == 0)
    {
      printf("%lld rows affected\n",
            mysql_affected_rows(mysql));
    }
    else  /* some error occurred */
    {
      printf("Could not retrieve result set\n");
      break;
    }
  }
  /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
  if ((status = mysql_next_result(mysql)) > 0)
    printf("Could not execute statement\n");
} while (status == 0);

mysql_close(mysql);