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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
关于   mysql_query()  

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

22.8.7.52. mysql_query()  
?
1
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 最好的方式是:  
?
1
2
MYSQL *mysql_conn;
mysql_conn = mysql_init( NULL );

关于mysql_connect()

mysql_connect() 属于老 API,已经被 mysql_real_connect() 所取代  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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    )  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
    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   

按如下方式可以设置重连的超时时间:  
?
1
2
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() 的注释如下:  
?
1
2
3
4
5
6
/* 
    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()】     
?
1
int mysql_library_init( int argc, char **argv, char **groups)

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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()  
?
1
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() 对后续查询的状态进行处理。  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 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() 一起使用。它不能与预处理语句接口一起使用。按照定义,预处理语句仅能与包含单个语句的字符串一起使用。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
关系型数据库 MySQL API
|
3月前
|
NoSQL 关系型数据库 MySQL
基于Python和mysql开发的智慧校园答题考试系统(源码+数据库+程序配置说明书+程序使用说明书)
基于Python和mysql开发的智慧校园答题考试系统(源码+数据库+程序配置说明书+程序使用说明书)
|
3月前
|
NoSQL 关系型数据库 MySQL
基于Python和mysql开发的BBS问答社区管理系统(源码+数据库+程序配置说明书+程序使用说明书)
基于Python和mysql开发的BBS问答社区管理系统(源码+数据库+程序配置说明书+程序使用说明书)
|
2月前
|
API 网络架构
解释 RESTful API,以及如何使用它构建 web 应用程序。
解释 RESTful API,以及如何使用它构建 web 应用程序。
88 0
|
3月前
|
JSON 关系型数据库 MySQL
这个问题是由于Flink的Table API在处理MySQL数据时,将MULTISET类型的字段转换为了JSON格式
【1月更文挑战第17天】【1月更文挑战第84篇】这个问题是由于Flink的Table API在处理MySQL数据时,将MULTISET类型的字段转换为了JSON格式
34 1
|
3月前
|
存储 SQL 关系型数据库
MySQL存储过程 if、case、while、loop、游标、变量、条件处理程序
MySQL存储过程 if、case、while、loop、游标、变量、条件处理程序
43 0
|
3月前
|
JSON API 数据格式
RESTful API,以及如何使用它构建 web 应用程序。
RESTful API,以及如何使用它构建 web 应用程序。
39 0
|
3月前
|
NoSQL 关系型数据库 MySQL
基于Python和mysql开发的商城购物管理系统分为前后端(源码+数据库+程序配置说明书+程序使用说明书)
基于Python和mysql开发的商城购物管理系统分为前后端(源码+数据库+程序配置说明书+程序使用说明书)
|
3月前
|
NoSQL 关系型数据库 MySQL
基于Python和mysql开发的在线音乐网站系统(源码+数据库+程序配置说明书+程序使用说明书)
基于Python和mysql开发的在线音乐网站系统(源码+数据库+程序配置说明书+程序使用说明书)
|
3月前
|
小程序 关系型数据库 MySQL
基于Python和mysql开发的看图猜成语微信小程序(源码+数据库+程序配置说明书+程序使用说明书)
基于Python和mysql开发的看图猜成语微信小程序(源码+数据库+程序配置说明书+程序使用说明书)