【原创】基于 MySQL Connector/C 实现客户端程序之 API 总结

原创
2013/12/20 17:31
阅读数 1.8K

关于 mysql_query()

下面是官网对于 mysql_query() 的说明。
===

22.8.7.52. mysql_query()
int mysql_query(MYSQL *mysql, const char *stmt_str)

功能:

  • 执行由 stmt_str 指定的 NULL 结尾的 SQL 语句。通常情况下,由 stmt_str 指定的内容是单条 SQL 语句,且结尾没有分号 ";" 或者 "\g" 。如果使能了多语句执行功能,那么由 stmt_str 指定的内容可以包含多条由分号分隔的语句;
  • mysql_query() 不能正确应用于包含二进制数据的语句,必须使用 mysql_real_query() 替代;(二进制数据中可能包含 "\0" 字符,会被 mysql_query() 按串结束符理解)
  • 如果你想知道执行后是否返回了结果集,你可以使用 mysql_field_count() 来检查;
  • 如果执行成功,则返回 0 ;非 0 为失败。


错误码:
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.
===

关于 mysql_init()
使用 mysql_init 最好的方式是:
MYSQL *mysql_conn; 
mysql_conn = mysql_init( NULL );

关于mysql_connect()

mysql_connect() 属于老 API,已经被 mysql_real_connect() 所取代
#ifdef USE_OLD_FUNCTIONS 
MYSQL * STDCALL 
mysql_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd) 
{
    MYSQL *res; 
    mysql=mysql_init(mysql); /* Make it thread safe */ 
    { 
        DBUG_ENTER("mysql_connect"); 
        if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) 
        { 
            if (mysql->free_me) 
                my_free(mysql); 
        }
        mysql->reconnect= 1;   // 直接设置了重连 
        DBUG_RETURN(res); 
    } 
} 
#endif

      调用 mysql_connect() 与 mysql_real_connect() 时输入参数意义相同,差别在于 mysql_connect() 中连接句柄可以为 NULL 。在这种情况下,C API 将自动为连接结构分配内存,并在调用 mysql_close() 时释放分配的内存。该方法的缺点是,如果连接失败,你无法检索错误消息。要想从 mysql_errno() 或 mysql_error() 获得错误消息,必须提供有效的 MYSQL 指针。而 mysql_real_connect() 中的连接句柄必须是已初始化过的 MYSQL 结构。

关于“重连”的设置
在 mysql_init() 的实现代码中有如下说明:  ( 以下内容取自 MySQL 5.6.10  )
/* 
    By default we don't reconnect because it could silently corrupt data (after 
    reconnection you potentially lose table locks, user variables, session 
    variables (transactions but they are specifically dealt with in 
    mysql_reconnect()). 
    This is a change: < 5.0.3 mysql->reconnect was set to 1 by default. 
    How this change impacts existing apps: 
    - existing apps which relyed on the default will see a behaviour change; 
    they will have to set reconnect=1 after mysql_real_connect(). 
    - existing apps which explicitely asked for reconnection (the only way they 
    could do it was by setting mysql.reconnect to 1 after mysql_real_connect()) 
    will not see a behaviour change. 
    - existing apps which explicitely asked for no reconnection 
    (mysql.reconnect=0) will not see a behaviour change. 
  */ 
  mysql->reconnect= 0;

      意思大概如下:默认情况下,我们不执行重连动作,因为可能会存在不易觉察的数据损坏(在重连后,有可能发生的情况包括,丢失表锁、用户变量、会话变量等(事务当然也会终止,但是会在 mysql_reconnect() 时得到正确处理))。

在 MySQL 5.0.3 版本之前,mysql->reconnect 默认被设置为 1 。
当前已经变更为默认设置 0 ,可能会产生的影响:
  • 依赖于默认值 1 的已存在应用程序将会感知到此变更,所以需要在调用 mysql_real_connect() 后自行设置 reconnect=1 ;
  • 原本就显式使能了重连功能的应用程序不会感受到此变更(一种可能是,其已经在调用 mysql_real_connect() 后自行设置了 reconnect=1);
  • 原本就显式去使能重连功能的应用程序不会感受到此变更(因为与当前默认行为已经一致)。
       综上,如果想要使能重连功能,正确的做法是,在调用 mysql_real_connect() 之后,通过 mysql_options() 显式设置 MYSQL_OPT_RECONNECT 的值为 1

按如下方式可以设置重连的超时时间:
unsigned int timeout = 10; 
mysql_options( mysql_conn, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout )

关于“字符集”的设置

目前存在三种方式设置字符集:
  • mysql_options( mysql_conn , MYSQL_SET_CHARSET_NAME , "utf8" );
  • mysql_set_character_set( mysql_conn, "utf8" )
  • mysql_query( mysql_conn, "SET NAMES utf8" )

首先看一下下面这段说明
===
使用 MySQL 字符集时的建议:
  • 建立数据库、表和进行数据库操作时尽量显式指出使用的字符集,而不是依赖于 MySQL 的默认设置,否则 MySQL 升级时可能带来很大困扰;
  • 数据库和连接字符集都使用 latin1 时,虽然大部分情况下都可以解决乱码问题,但缺点是无法以字符为单位来进行 SQL 操作,一般情况下将数据库和连接字符集都置为 utf8 是较好的选择;
  • 使用 MySQl C API 时,初始化数据库句柄后马上通过 mysql_options() 设定 MYSQL_SET_CHARSET_NAME 属性为 utf8 ,这样就不用再显式地执行 "SET NAMES utf8" 语句指定连接字符集,且用 mysql_ping 重连断开的长连接时也会把连接字符集重置为 utf8 ;
其他注意事项
  • my.cnf 中的 default_character_set 设置只影响 mysql 命令连接服务器时的连接字符集,不会对使用 libmysqlclient 库的应用程序产生任何作用!
  • 对字段进行的 SQL 函数操作通常都是以内部操作字符集进行的,不受连接字符集设置的影响。
  • SQL 语句中的裸字符串会受到连接字符集或 introducer 设置的影响,对于比较之类的操作可能产生完全不同的结果,需要小心!

MySQL 5.6.10 源码中对 mysql_set_character_set() 的注释如下:
/*  
   mysql_set_character_set function sends SET NAMES cs_name to 
   the server (which changes character_set_client, character_set_result 
   and character_set_connection) and updates mysql->charset so other 
   functions like mysql_real_escape will work correctly. 
*/

      mysql_set_character_set() 会向服务器发送 "SET NAMES xxx" 命令来设置字符集信息(该设置会改变 character_set_client、character_set_result 和 character_set_connection 的值),并会更新 mysql->charset 的值,进而对 mysql_real_escape() 等函数产生影响。

该函数内部会
  • 调用 mysql_options(..., MYSQL_SET_CHARSET_NAME, ...); 设置 mysql->options.charset_name 的值
  • 调用 mysql_real_query(); 来发送 "SET NAMES xxx" 

关于 mysql_library_init()和mysql_library_end()
问题:是否一定需要调用 mysql_library_init() 和 mysql_library_end() 函数?如何设置 mysql_library_init() 的入参?

回答: 通过调用 mysql_library_init(),可以初始化 MySQL 库。库可以是 mysqlclient C 客户端库,或 mysqld 嵌入式服务器库。

      调用 mysql_library_init() 和 mysql_library_end() 的目的在于,为 MySQL 库提供恰当的初始化和结束处理。对于与客户端库链接的应用程序,它们提供了改进的内存管理功能。如果不调用 mysql_library_end(),内存块仍将保持分配状态(这不会增加应用程序使用的内存量,但某些内存泄漏检测器将抗议它)。对于与嵌入式服务器链接的应用程序,这些调用会启动并停止服务器。

      mysql_library_init() 和 mysql_library_end() 实际上是 #define 符号,这类符号使得它们等效于mysql_server_init() 和 mysql_server_end(),但其名称更清楚地指明,无论应用程序使用的是 mysqlclient 或mysqld 库,启动或结束 MySQL 库时,应调用它们。对于早期的 MySQL 版本,可调用 mysql_server_init() 和mysql_server_end() 取而代之。

调用 mysql_library_init() 时如何设置入参?可以参考如下官网信息:【 22.8.7.40. mysql_library_init()  
int mysql_library_init(int argc, char **argv, char **groups)

对于常规的客户端应用程序来说,通常以 mysql_library_init(0, NULL, NULL) 进行调用。

#include <mysql.h> 
#include <stdlib.h> 

int main(void) { 
  if (mysql_library_init(0, NULL, NULL)) { 
    fprintf(stderr, "could not initialize MySQL library\n"); 
    exit(1); 
  } 

  /* Use any MySQL API functions here */ 
  mysql_library_end(); 

  return EXIT_SUCCESS; 
}

The groups argument is an array of strings that indicate the groups in option files from which to read options. For convenience, if the groups argument itself is NULL, the [server] and [embedded] groups are used by default.


关于mysql_next_result()
int mysql_next_result(MYSQL *mysql)

      如果存在多个查询结果,mysql_next_result() 将读取下一个查询结果,并将状态返回给应用程序。如果前面的查询返回了结果集,必须为其调用 mysql_free_result()。

      调用 mysql_next_result() 后,连接状态就像你已为下一查询调用了 mysql_real_query() 或 mysql_query() 一样。这意味着你能调用 mysql_store_result()、mysql_warning_count()、mysql_affected_rows() 等等。

      如果 mysql_next_result() 返回错误,将不执行任何其他语句,也不会获取任何更多的结果。

返回值
描述
0 成功并有多个结果
-1 成功但没有多个结果
>0 出错

错误码:
CR_COMMANDS_OUT_OF_SYNC
以不恰当的顺序执行了命令。例如,没有为前面的结果集调用mysql_use_result()。
CR_SERVER_GONE_ERROR
MySQL服务器不可用。
CR_SERVER_LOST
在查询过程中,与服务器的连接丢失。
CR_UNKNOWN_ERROR
出现未知错误。

官网说明
===

两种场景下使用:
  • 在单字串中执行了多条语句时;
  • 通过 CALL 语句执行存储过程时。
      mysql_next_result() 会读取下一条语句执行的结果,并通过返回值表明是否还存在下一条结果。如果 mysql_next_result() 返回错误,怎么没有更多的结果了。
      在每一次调用 mysql_next_result() 之前,必须针对当前已经返回结果集的语句调用 mysql_free_result() 以释放结果集所占内存。
      在调用 mysql_next_result() 之后,当前连接上的状态就像再次调用 mysql_real_query() 或 mysql_query() 执行了下一条语句一样。所以,你可以继续调用 mysql_store_result()、mysql_warning_count()、mysql_affected_rows() 等函数。
      如果通过 CALL 语句来执行存储过程,则必须使能 CLIENT_MULTI_RESULTS 标识。因为在此场景下,执行 CALL 语句会获得一条 CALL 调用状态的结果,和若干条与存储过程中所含的 sql 语句对应的结果集。因为 CALL 会返回多个结果信息,故通常会在循环中调用 mysql_next_result() 来确定是否还有更多的结果待处理。  
      可以在调用 mysql_real_connect() 中使能 CLIENT_MULTI_RESULTS 标识,直接的方式就是传 CLIENT_MULTI_RESULTS 标识本身,间接的方式是传 CLIENT_MULTI_STATEMENTS 标识(会间接使能 CLIENT_MULTI_RESULTS)。在 MySQL 5.6 中,CLIENT_MULTI_RESULTS 标识已经被默认使能。  
      使用 mysql_more_results() 来测试是否还有更多的结果待处理是可以的。然而,该函数不会变更当前连接的状态,所以即使其返回了 true ,你仍旧必须调用 mysql_next_result() 来切换到获取下一结果的状态。  
===

关于多查询执行的C API处理
       MySQL 5.1 支持在单个查询字符串中指定多语句执行。要想与给定的连接一起使用该功能,打开连接时,必须将标志参数中的 CLIENT_MULTI_STATEMENTS 选项指定给 mysql_real_connect() 。也可以通过调用 mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON),为已有的连接设置它。  
在默认情况下,mysql_query() 和 mysql_real_query() 仅返回第 1 个查询的状态,并能使用 mysql_more_results() 和 mysql_next_result() 对后续查询的状态进行处理。
/* Connect to server with option CLIENT_MULTI_STATEMENTS */ 
mysql_real_connect(..., CLIENT_MULTI_STATEMENTS); 
  
/* Now execute multiple queries */ 
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"); 
do 
{ 
  /* Process all results */ 
  ... 
  printf("total affected rows: %lld", mysql_affected_rows(mysql)); 
  ... 
  if (!(result= mysql_store_result(mysql))) 
  { 
     printf(stderr, "Got fatal error processing query\n"); 
     exit(1); 
  } 
  process_result_set(result); /* client function */ 
  mysql_free_result(result); 
} while (!mysql_next_result(mysql));

      多语句功能可与 mysql_query() 或 mysql_real_query() 一起使用。它不能与预处理语句接口一起使用。按照定义,预处理语句仅能与包含单个语句的字符串一起使用。


展开阅读全文
打赏
0
9 收藏
分享
加载中
总结的很好。79
2015/04/28 14:55
回复
举报
更多评论
打赏
1 评论
9 收藏
0
分享
返回顶部
顶部