当前位置: 首页 > article >正文

【MySQL】C语言连接MySQL数据库2——基本API的学习

目录

1.MYSQL的相关变量

1.1. MYSQL变量

1.2.MYSQL_RES变量

1.3.MYSQL变量和MYSQL_RES变量的关系

2.MYSQL的最基本的四个API

2.1.mysql_init()函数

2.2.mysql_real_connect()函数

2.3.mysql_set_character_set()——设置连接的编码格式

2.4.mysql_close()——关闭数据库连接

 3.MYSQL的几个SQL请求API

3.1.mysql_query() 

3.2.mysql_real_query()

3.3.使用示例

4.MYSQL的几个获取查询结果的C API

4.1.mysql_store_result()

4.2.mysql_use_result()

4.4.mysql_store_result()和mysql_use_result()的区别

4.3.获取查询结果的行数——mysql_num_rows() 

4.4.获取查询结果的列数——mysql_num_fields()

4.5.获取查询结果的列属性——mysql_fetch_fields()

4.6.获取查询结果中的一行数据——mysql_fetch_rows()

4.7.释放结果集——mysql_free_result()

4.8.使用示例


 

1.MYSQL的相关变量

1.1. MYSQL变量

连接数据库前,必须先创建MYSQL变量,此变量在很多Mysql API函数会用到。它包含了一些连接信息等数据。

typedef struct st_mysql {
NET  net;            /* Communication parameters 通讯参数,网络相关*/
unsigned char connector_fd;   /* ConnectorFd for SSL 加密套接字协议层*/

char  *host,*user,*passwd,*unix_socket,*server_version,
*host_info,*info,*db;//数据库用户名,密码,主机名,Unix套接字,版本,主机信息

unsigned int  port,client_flag,server_capabilities;
unsigned int  protocol_version;
unsigned int  field_count;
unsigned int  server_status;
unsigned long thread_id;      /* Id for connection in server */
my_ulonglong affected_rows;
my_ulonglong insert_id;      /* id if insert on table with NEXTNR */
my_ulonglong extra_info;              /* Used by mysqlshow */
unsigned long packet_length;
enum mysql_status status;
MYSQL_FIELD   *fields;
MEM_ROOT      field_alloc;
my_bool       free_me;        /* If free in mysql_close */
my_bool       reconnect;      /* set to 1 if automatic reconnect */
struct st_mysql_options options;
char          scramble_buff[9];
struct charset_info_st *charset;
unsigned int  server_language;
} MYSQL;

它用于表示与 MySQL 数据库的连接状态和相关参数。这个结构体通常用于 MySQL C API 中,允许开发者通过 C 程序与 MySQL 数据库进行交互。

以下是对该结构体中各个成员的简要说明:

  1. NET net;:包含与服务器通信的网络参数,如套接字、缓冲区等。
  2. unsigned char connector_fd;:用于 SSL 加密的套接字文件描述符。
  3. char *host, *user, *passwd, *unix_socket, *server_version, *host_info, *info, *db;:分别指向数据库主机名、用户名、密码、Unix 套接字路径、服务器版本、主机信息、额外信息和当前数据库名的字符串。
  4. unsigned int port, client_flag, server_capabilities, protocol_version, field_count, server_status;:分别表示端口号、客户端标志、服务器能力、协议版本、字段计数和服务器状态。
  5. unsigned long thread_id;:在服务器中用于标识此连接的线程 ID。
  6. my_ulonglong affected_rows, insert_id, extra_info;:分别表示受影响的行数、插入时的 ID 和额外信息(如 mysqlshow 使用)。
  7. unsigned long packet_length;:当前数据包的长度。
  8. enum mysql_status status;:表示连接的状态。
  9. MYSQL_FIELD *fields;:指向字段信息的指针数组。
  10. MEM_ROOT field_alloc;:用于字段信息分配的内存根。
  11. my_bool free_me, reconnect;:布尔值,分别表示在 mysql_close 时是否释放此结构体和是否启用自动重连。
  12. struct st_mysql_options options;:包含连接选项的结构体。
  13. char scramble_buff[9];:用于加密的缓冲区。
  14. struct charset_info_st *charset;:指向字符集信息的指针。
  15. unsigned int server_language;:服务器使用的语言。

这个结构体通常在使用 MySQL C API 函数如 mysql_real_connect、mysql_query、mysql_store_result 等时作为参数传递,以管理数据库连接和查询结果。

1.2.MYSQL_RES变量

        MYSQL_RES结构体中包含了查询结果集,也就是从数据库中查询到的数据。可以使用mysql_store_result或mysql_use_result函数获得。这个结构代表返回行的一个查询的(SELECT, SHOW, DESCRIBE, EXPLAIN)的结果。返回的数据称为“数据集”,在C的API里对应的就是MYSQL_RES,从数据库读取数据,最后就是从MYSQL_RES中读取数据。

typedef struct st_mysql_res {
  my_ulonglong  row_count;
  MYSQL_FIELD   *fields;
  MYSQL_DATA    *data;
  MYSQL_ROWS    *data_cursor;
  unsigned long *lengths;       /* column lengths of current row */
  MYSQL     *handle;        /* for unbuffered reads */
  const struct st_mysql_methods *methods;
  MYSQL_ROW row;            /* If unbuffered read */
  MYSQL_ROW current_row;        /* buffer to current row */
  MEM_ROOT  field_alloc;
  unsigned int  field_count, current_field;
  my_bool   eof;            /* Used by mysql_fetch_row */
  /* mysql_stmt_close() had to cancel this result */
  my_bool       unbuffered_fetch_cancelled;  
  void *extension;
} MYSQL_RES;

它通常用于 MySQL C API 中,以表示查询结果集。以下是对该结构体中各个成员的简要说明:

  1. my_ulonglong row_count;:表示结果集中的行数(对于 SELECT 语句而言)。
  2. MYSQL_FIELD *fields;:指向一个 MYSQL_FIELD 结构体数组的指针,其中每个 MYSQL_FIELD 结构体都描述了结果集中的一个字段(如字段名、类型、长度等)。
  3. MYSQL_DATA *data;:指向包含实际数据行的 MYSQL_DATA 结构体的指针。
  4. MYSQL_ROWS *data_cursor;:当前指向结果集中某一行的指针(用于遍历结果集)。
  5. unsigned long *lengths;:指向一个数组的指针,该数组包含当前行中各字段的长度。
  6. MYSQL *handle;:指向与结果集关联的 MYSQL 结构体的指针,该结构体表示与 MySQL 服务器的连接。
  7. const struct st_mysql_methods *methods;:指向一个包含用于操作结果集的方法(函数指针)的结构体的指针。
  8. MYSQL_ROW row;:用于未缓冲读取时存储当前行的数据。
  9. MYSQL_ROW current_row;:指向当前正在处理的行的数据的指针(可能是一个内部缓冲区)。
  10. MEM_ROOT field_alloc;:用于字段信息分配的内存根(与内存管理相关)。
  11. unsigned int field_count, current_field;:分别表示结果集中的字段数和当前正在处理的字段的索引。
  12. my_bool eof;:一个布尔值,用于指示是否已到达结果集的末尾(在遍历结果集时使用)。
  13. my_bool unbuffered_fetch_cancelled;:一个布尔值,用于指示是否取消了未缓冲的读取操作(这通常发生在语句被关闭时)。
  14. void *extension;:一个指向扩展数据的指针,可能用于将来的扩展或特定于实现的用途。

在使用 MySQL C API 时,您通常会执行以下步骤来处理查询结果:

  1. 使用 mysql_query() 发送 SQL 查询。
  2. 使用 mysql_store_result() 或 mysql_use_result() 获取查询结果集(MYSQL_RES 类型的对象)。
  3. 使用 mysql_fetch_row() 遍历结果集中的每一行。
  4. 对于每一行,可以使用 mysql_fetch_fields() 获取字段信息,或使用 mysql_fetch_lengths() 获取当前行中各字段的长度。
  5. 完成操作后,使用 mysql_free_result() 释放结果集。

1.3.MYSQL变量和MYSQL_RES变量的关系

        MYSQL 和 MYSQL_RES 在 MySQL C API 中是两个不同的结构体,它们各自承担着不同的角色。

  1. MYSQL 结构体代表与 MySQL 服务器的连接。它包含了与连接相关的所有信息,如主机名、用户名、密码、端口号、连接状态、字符集等。当您使用 MySQL C API 时,您会首先创建一个 MYSQL 结构体实例,并使用它来建立与 MySQL 服务器的连接。
  2. MYSQL_RES 结构体则代表从 MySQL 服务器检索到的查询结果集。当您执行一个查询(如 SELECT 语句)时,MySQL 服务器会返回一个结果集,该结果集在 C API 中由 MYSQL_RES 结构体表示。您可以使用 MYSQL_RES 结构体来遍历查询结果,并获取每一行和每一列的数据。

在 MySQL C API 的使用中,MYSQL 和 MYSQL_RES 通常是这样协作的:

  1. 您创建一个 MYSQL 结构体实例,并使用它建立与 MySQL 服务器的连接。
  2. 您使用连接实例(MYSQL 结构体)执行一个查询。
  3. 如果查询成功,MySQL 服务器会返回一个结果集,该结果集由 MYSQL_RES 结构体表示。您可以通过调用 API 函数(如 mysql_store_result 或 mysql_use_result)来获取这个结果集。
  4. 您使用 MYSQL_RES 结构体来遍历查询结果,并处理每一行和每一列的数据。
  5. 当您完成查询结果的处理后,您需要释放 MYSQL_RES 结构体以释放资源。
  6. 当您完成与 MySQL 服务器的所有交互后,您需要关闭连接并释放 MYSQL 结构体。


2.MYSQL的最基本的四个API

2.1.mysql_init()函数

在连接数据库之前,需要先创建一个MySQL对象,进行初始化。那怎么初始化呢?就用下面这个函数!!

MYSQL *mysql_init(MYSQL *mysql)

描述

  1. 该函数用来分配或者初始化一个MySQL对象
  2. 如果传入参数为NULL,mysql_init将自动分配一个MYSQL对象(句柄)并返回
  3. 如果传入的是一个MYSQL对象,该函数会对该对象进行初始化

返回值

  • 初始化的MYSQL*句柄。如果无足够内存以分配新的对象,返回NULL。

错误

  • 在内存不足的情况下,返回NULL。

接下来我们来看看两个例子,来理解它的用法

当然,mysql_init() 函数是 MySQL C API 中用于初始化一个 MYSQL 对象的函数。下面通过两个例子来介绍它的用法:

  • 例子 1:传入 NULL 参数

在这个例子中,我们传入 NULL 给 mysql_init() 函数,它会为我们分配一个新的 MYSQL 对象并返回其指针。

 #include <mysql/mysql.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
   int main() {  
 // 传入 NULL,mysql_init 会分配一个新的 MYSQL 对象  
 MYSQL *conn = mysql_init(NULL);  
   // 检查是否分配成功  
 if (conn == NULL) {  
 fprintf(stderr, "mysql_init() failed\n");  
 return EXIT_FAILURE;  
 }  
   // 在这里可以进一步配置 conn 对象,例如设置服务器地址、用户名、密码等  
 // mysql_real_connect(conn, "host", "user", "password", "database");  
   // 释放 MYSQL 对象  
 mysql_close(conn);  
   return EXIT_SUCCESS;  
 } 

在这个例子中,mysql_init(NULL) 会分配一个新的 MYSQL 对象并返回其指针。如果内存分配失败,它会返回 NULL。然后我们检查返回值是否为 NULL,如果是,则输出错误信息并退出程序。最后,无论是否成功连接到数据库,我们都应该调用 mysql_close() 来释放 MYSQL 对象。

  • 例子 2:传入一个已分配的 MYSQL 对象

在这个例子中,我们先手动分配一个 MYSQL 对象,然后传入该对象给 mysql_init() 函数进行初始化。

#include <mysql/mysql.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
   int main() {  
 // 手动分配一个 MYSQL 对象  
 MYSQL conn;  
 memset(&conn, 0, sizeof(conn)); // 初始化内存为0(可选,但推荐)  
   // 传入已分配的 MYSQL 对象进行初始化  
 if (mysql_init(&conn) == NULL) {  
 fprintf(stderr, "mysql_init() failed\n");  
 return EXIT_FAILURE;  
 }  
   // 在这里可以进一步配置 conn 对象,例如设置服务器地址、用户名、密码等  
 // mysql_real_connect(&conn, "host", "user", "password", "database");  
   // 释放 MYSQL 对象(注意:这里不需要调用 mysql_close(&conn),因为 conn 是在栈上分配的)  
 // 但由于 mysql_init() 初始化了内部状态,通常建议在使用完毕后重置或清理状态  
 // 在这个例子中,由于 conn 是在栈上分配的,它会在函数返回时自动销毁  
   return EXIT_SUCCESS;  
 } 

在这个例子中,我们手动分配了一个 MYSQL 对象 conn,并使用 memset() 将其内存初始化为 0(这一步是可选的,但推荐这样做以避免未定义行为)。然后,我们传入 conn 给 mysql_init() 函数进行初始化。如果初始化失败,它会返回 NULL,我们检查并处理错误。

        需要注意的是,由于 conn 是在栈上分配的,它会在函数返回时自动销毁。因此,在这个例子中,我们不需要调用 mysql_close(&conn) 来释放它。但是,由于 mysql_init() 初始化了 MYSQL 对象的内部状态,通常建议在使用完毕后重置或清理状态(尽管在这个简单的例子中我们并没有这样做)。

        在实际应用中,更常见的是使用第一个例子中的方法,即传入 NULL 让 mysql_init() 分配一个新的 MYSQL 对象,这样可以避免手动管理内存。 


2.2.mysql_real_connect()函数

创建完MySQL对象后就可以连接数据库了,连接数据库的函数如下:

MYSQL *  mysql_real_connect(MYSQL *mysql,
                           const char *host,
                           const char *user,
                           const char *passwd,
                           const char *db,
                           unsigned int port,
                           const char *unix_socket,
                           unsigned long client_flag)

参数说明

  1. mysql
    • 类型:MYSQL*
    • 说明:指向已初始化的MYSQL结构的指针。
    • 注意:在调用mysql_real_connect()之前,必须调用mysql_init()来初始化MYSQL结构。
  2. host
    • 类型:const char *
    • 说明:主机名或IP地址。
    • 取值:
      • NULL或"localhost":表示连接到本地主机。
      • 其他字符串:表示要连接到的MySQL服务器的主机名或IP地址。
    • 注意:如果操作系统支持,可能会使用套接字或命名管道而不是TCP/IP连接到服务器。
  3. user
    • 类型:const char *
    • 说明:MySQL登录ID。
    • 取值:
      • NULL或空字符串"":表示使用当前用户。
      • 其他字符串:表示MySQL服务器的用户名。
    • 注意:在Windows ODBC下,必须明确指定当前用户名。
  4. passwd
    • 类型:const char *
    • 说明:用户的密码。
    • 取值:
      • NULL:表示用户的密码字段为空,允许数据库管理员根据用户是否拥有指定密码来设置不同的权限。
      • 其他字符串:表示用户的密码。
    • 注意:不要尝试在调用mysql_real_connect()之前加密密码,密码加密将由客户端API自动处理。
  5. db
    • 类型:const char *
    • 说明:数据库名称。
    • 取值:
      • NULL:表示不设置默认数据库。
      • 其他字符串:表示要连接的数据库名称。
  6. port
    • 类型:unsigned int
    • 说明:TCP/IP连接的端口号。
    • 取值:
      • 0:表示使用默认端口号。
      • 其他值:表示要使用的端口号。
    • 注意:host参数决定了连接的类型。
  7. unix_socket
    • 类型:const char *
    • 说明:要使用的套接字或命名管道。
    • 取值:
      • NULL:表示不使用套接字或命名管道。
      • 其他字符串:表示要使用的套接字或命名管道的路径。
    • 注意:host参数决定了连接的类型。
  8. client_flag
    • 类型:unsigned long
    • 说明:允许特定功能的标志组合。
    • 取值:可以是多个标志的组合,如CLIENT_COMPRESS、CLIENT_FOUND_ROWS等。
    •  client_flag的值通常为0,但是,也能将其设置为下述标志的组合,以允许特定功能:

标志名称

标志描述

CLIENT_COMPRESS

使用压缩协议。

CLIENT_FOUND_ROWS

返回发现的行数(匹配的),而不是受影响的行数。

CLIENT_IGNORE_SPACE

允许在函数名后使用空格。使所有的函数名成为保留字。

CLIENT_INTERACTIVE

关闭连接之前,允许interactive_timeout(取代了wait_timeout)秒的不活动时间。客户端的会话wait_timeout变量被设为会话interactive_timeout变量的值。

CLIENT_LOCAL_FILES

允许LOAD DATA LOCAL处理功能。

CLIENT_MULTI_STATEMENTS

通知服务器,客户端可能在单个字符串内发送多条语句(由‘;’隔开)。如果未设置该标志,将禁止多语句执行。

CLIENT_MULTI_RESULTS

通知服务器,客户端能够处理来自多语句执行或存储程序的多个结果集。如果设置了CLIENT_MULTI_STATEMENTS,将自动设置它。

CLIENT_NO_SCHEMA

禁止db_name.tbl_name.col_name语法。它用于ODBC。如果使用了该语法,它会使分析程序生成错误,在捕获某些ODBC程序中的缺陷时,它很有用。

CLIENT_ODBC

客户端是ODBC客户端。它将mysqld变得更为ODBC友好。

CLIENT_SSL

使用SSL(加密协议)。该选项不应由应用程序设置,它是在客户端库内部设置的。


 以下是一个使用mysql_real_connect()函数连接到MySQL数据库的示例代码:

#include <mysql/mysql.h>  // 引入MySQL客户端库的头文件  
#include <stdio.h>        // 引入标准输入输出库的头文件  
#include <stdlib.h>       // 引入标准库的头文件,用于EXIT_SUCCESS和EXIT_FAILURE宏  
  
int main() {  
    MYSQL *conn;          // 声明一个MYSQL类型的指针,用于存储数据库连接  
  
    // 初始化MySQL连接句柄  
    conn = mysql_init(NULL);  
      
    // 检查mysql_init()是否成功  
    if (conn == NULL) {     
        // 如果conn为NULL,说明初始化失败  
        fprintf(stderr, "mysql_init() failed\n"); // 向标准错误输出错误信息  
        return EXIT_FAILURE;                     // 返回失败状态码  
    }  
  
    // 尝试连接到MySQL数据库  
    if (mysql_real_connect(conn, "localhost", "user", "password", "database", 0, NULL, 0) == NULL) {  
        // 如果mysql_real_connect()返回NULL,说明连接失败  
        fprintf(stderr, "mysql_real_connect() failed\n"); // 向标准错误输出错误信息  
        mysql_close(conn);                             // 关闭MySQL连接句柄,释放资源  
        return EXIT_FAILURE;                           // 返回失败状态码  
    }  
  
    // 如果到这里,说明连接成功  
    // 在这里可以执行数据库操作,例如查询、插入、更新等  
    // 例如:mysql_query(conn, "SELECT * FROM some_table");  
    // 注意:执行数据库操作后,通常需要检查操作是否成功,并处理结果集  
  
    // 连接使用完毕后,关闭MySQL连接句柄  
    mysql_close(conn);  
  
    // 程序正常结束,返回成功状态码  
    return EXIT_SUCCESS;  
}

在上面的示例中,我们首先使用mysql_init()函数初始化了一个MYSQL对象,然后使用mysql_real_connect()函数尝试连接到MySQL服务器。

  1. 如果连接失败,我们输出错误信息并关闭连接;
  2. 如果连接成功,则可以在这里执行数据库操作(如查询、插入等)。最后,我们使用mysql_close()函数关闭连接并释放资源。

2.3.mysql_set_character_set()——设置连接的编码格式

        当我们使用mysql_real_connect建立好链接之后,获取英文字符是没有问题,但是如果是获取中文则是乱码,因为MySQL默认连接的编码格式采用latin1,所以我们需要设置连接的编码格式。

         mysql_set_character_set() 这个函数是 MySQL C API 的一部分,用于为当前数据库连接设置默认的字符集。以下是对您所提供信息的详细解释和补充:

函数原型

 int mysql_set_character_set(MYSQL *mysql, const char *csname); 


参数

  1. MYSQL *mysql: 这是一个指向已经初始化并成功连接到 MySQL 服务器的 MYSQL 对象的指针。
  2. const char *csname: 这是一个指向以空字符结尾的字符串的指针,该字符串包含了要设置为默认字符集的名称。这个字符集名称必须是 MySQL 服务器支持的字符集之一。

功能描述

mysql_set_character_set() 函数用于为当前数据库连接设置默认的字符集。当这个函数被调用时,它会执行以下操作:

  1. 将连接的字符集更改为由 csname 参数指定的字符集。
  2. 将连接的校对集更改为该字符集的默认校对集。
  3. 更新 mysql 对象的 charset 成员变量,以反映新的字符集设置。这个更新会影响到 mysql_real_escape_string() 函数的行为,因为该函数会使用 mysql 对象的 charset 成员变量来确定如何对字符串进行转义。

返回值

  • 如果函数成功执行,则返回 0。
  • 如果函数执行失败(例如,因为指定的字符集名称无效),则返回一个非零值。在这种情况下,您可以使用 mysql_error() 函数来获取更详细的错误信息。

这个函数通常在建立数据库连接后立即调用,以确保后续的数据库操作使用正确的字符集和校对集。这对于处理包含特殊字符或多字节字符集(如 UTF-8)的数据非常重要。
示例代码

以下是一个使用 mysql_set_character_set() 函数的简单示例:

#include <mysql/mysql.h>  
#include <stdio.h>  
#include <stdlib.h>  
  
int main() {  
    MYSQL *conn;  
    conn = mysql_init(NULL);  
  
    if (conn == NULL) {  
        fprintf(stderr, "mysql_init() failed\n");  
        return EXIT_FAILURE;  
    }  
  
    if (mysql_real_connect(conn, "localhost", "user", "password", "database", 0, NULL, 0) == NULL) {  
        fprintf(stderr, "mysql_real_connect() failed\n");  
        mysql_close(conn);  
        return EXIT_FAILURE;  
    }  
  
    if (mysql_set_character_set(conn, "utf8mb4") != 0) {  
        fprintf(stderr, "mysql_set_character_set() failed. Error: %s\n", mysql_error(conn));  
        mysql_close(conn);  
        return EXIT_FAILURE;  
    }  
  
    // 连接成功,字符集设置成功,可以在这里执行数据库操作  
  
    mysql_close(conn);  
    return EXIT_SUCCESS;  
}

在这个示例中,我们首先初始化了一个 MYSQL 对象,然后尝试连接到 MySQL 服务器。连接成功后,我们调用 mysql_set_character_set() 函数将连接的字符集设置为 utf8mb4。如果设置失败,我们会打印出错误信息并关闭连接。如果设置成功,我们就可以继续执行数据库操作了。


2.4.mysql_close()——关闭数据库连接

与数据库交互完毕后,需要关闭数据库连接释放资源,关闭数据库连接的函数如下:

 void mysql_close(MYSQL *mysql); 

参数

  • MYSQL *mysql: 这是一个指向已经初始化并成功连接到MySQL服务器的MYSQL对象的指针。这个对象通常是通过调用mysql_init()或mysql_real_connect()等函数获得的。

功能描述

  • mysql_close()函数用于关闭与MySQL服务器的连接,并释放与该连接相关的所有资源。这包括网络套接字、内存分配等。一旦调用了这个函数,MYSQL对象就不再有效,不应该再被用于任何MySQL API函数。

使用场景

  • mysql_close()函数应该在完成所有数据库操作后调用,以确保所有资源都被正确释放。这通常是在程序的末尾或在一个数据库操作块的末尾进行的。

注意事项

  1. 在调用mysql_close()之前,确保没有未完成的查询或结果集需要处理。
  2. 一旦调用了mysql_close(),就不要再尝试使用与该连接相关的MYSQL对象了。
  3. 如果在调用mysql_close()之前程序异常终止,操作系统通常会回收所有资源,但显式关闭连接是一个更好的做法,因为它可以确保资源被及时释放,并且可以避免潜在的内存泄漏。

现在,我们可以来测试一下,到底能不能连接上我们的MYSQL数据库!!

 我们先到我们的数据库里面设置一个用户,如果你只想允许john从本地主机连接到数据库服务器,你可以使用以下SQL语句:

CREATE USER 'john'@'localhost' IDENTIFIED BY '123456';

注意我们要给这个用户john一些权限!!

GRANT ALL PRIVILEGES ON *.* TO 'john'@'localhost' WITH GRANT OPTION;
 
FLUSH PRIVILEGES;

 接下来我们运行这个程序

main.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <mysql/mysql.h>

const char* host = "localhost";
const char* user = "john";
const char* password = "123456";
const char* db = "db1";
u_int32_t port = 3306;

int main()
{
    // 1.初始化MYSQL对象
    MYSQL* my = mysql_init(NULL);
    if (!my)
    {
        perror("mysql_init fail: ");
        exit(errno);
    }

    // 2.连接MySQL服务器
    if (!mysql_real_connect(my, host, user, password, db, port, NULL, 0))
    {
        perror("mysql_real_connect fail: ");
        exit(errno);
    }

    // 3. 设置连接的编码格式
    if (0 != mysql_set_character_set(my, "utf8"))
    {
        perror("mysql_set_character_set fail: ");
        exit(errno);
    }

    // 休眠10s,方便我们观察实验现象
    sleep(10);

    // 4. 关闭连接,释放对象
    mysql_close(my);
    
    return 0;
}

makefile 

test:main.c
    gcc -o $@ $^ -std=c99 -L /usr/lib/x86_64-linux-gnu -lmysqlclient
.PHONY:clean
    rm -rf test

 运行之后,我们可以使用下面这个命令来查看当前数据库有几个用户在线!!

show processlist;

10秒之后

 很好!!我们就是连接成功了!

 3.MYSQL的几个SQL请求API

3.1.mysql_query() 

        与数据库建立连接期间,就可以向MySQL服务器下发SQL请求,下发SQL请求的函数 mysql_query() 函数,它是 MySQL C API 中的一个函数,用于执行由空字符('\0')终结的字符串所指向的 SQL 查询。

函数原型

int mysql_query(MYSQL *mysql, const char *query); 

参数

  1. MYSQL *mysql: 这是一个指向已经初始化并成功连接到 MySQL 服务器的 MYSQL 对象的指针。
  2. const char *query: 这是一个指向包含要执行的 SQL 语句的空字符终结字符串的指针。通常,这个字符串应该只包含一条 SQL 语句,并且不应该以分号(';')或 '\g' 结尾。然而,如果启用了多语句执行(通过 mysql_set_server_option(mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON)),则可以在这个字符串中包含多条由分号隔开的 SQL 语句。

功能描述

  • mysql_query() 函数用于执行 SQL 查询。它接受一个 SQL 语句字符串,并将其发送到 MySQL 服务器以执行。这个函数是执行 SQL 语句的高级接口,它简化了对 SQL 语句的发送和执行过程,但也有一些限制,比如不能用于包含二进制数据的查询(因为二进制数据可能包含空字符,而 mysql_query() 会将空字符解释为查询字符串的结束)

返回值

  1. 如果查询成功执行,则返回 0。
  2. 如果查询执行失败,则返回一个非零值。在这种情况下,你可以使用 mysql_error() 函数来获取更详细的错误信息。

此外,注意一下几点

线程安全性:

  • mysql_query() 是线程安全的,这意味着您可以在多线程程序中使用它,但每个线程应该有自己的 MYSQL 连接对象。

多语句执行:

  • 默认情况下,mysql_query() 只能执行一条 SQL 语句。但是,如果启用了多语句执行(通过 mysql_set_server_option(mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON)),则可以在一个查询字符串中包含多条 SQL 语句。
  • 需要注意的是,多语句执行可能会增加 SQL 注入的风险,因此在使用时应格外小心。
  • 特别注意:mysql_query() 不适用于包含二进制数据的查询。

mysql_query函数在处理包含特定二进制数据的SQL语句时可能会遇到问题,这主要是因为mysql_query函数会将其输入参数视为以空字符(\0)结尾的C字符串。二进制数据中可能包含\0字符,这在C字符串中被用作字符串的终止符。因此,当mysql_query遇到\0字符时,它会错误地认为字符串已经结束,从而可能导致SQL语句的截断或不完整执行。

具体来说,以下类型的二进制数据可能会导致mysql_query函数无法正确处理:

  1. 包含\0字符的字符串:如果二进制数据中包含\0字符,那么mysql_query可能会在\0处截断字符串,导致SQL语句不完整。
  2. 非文本二进制数据:对于非文本数据(如图像、音频或视频文件),它们通常以二进制形式存储,并且可能包含许多\0字符。这些数据如果直接传递给mysql_query,会导致函数无法正确解析和处理。
  3. 经过特定编码或压缩的二进制数据:某些二进制数据可能经过特定的编码或压缩处理,这些处理可能会在数据中引入\0字符或其他特殊字符。如果这些数据直接用于SQL语句,同样可能导致mysql_query函数无法正确执行。

为了避免这些问题,当需要处理包含二进制数据的SQL语句时,应使用mysql_real_query函数。与mysql_query不同,mysql_real_query接受一个额外的参数来指定SQL语句的长度,从而能够正确地处理包含\0字符的二进制数据。此外,mysql_real_query也通常比mysql_query更快,因为它不需要在内部调用strlen函数来计算字符串的长度。

3.2.mysql_real_query()

        mysql_real_query() 是 MySQL C API 中的一个函数,用于执行 SQL 语句。这个函数允许你将一个包含 SQL 语句的字符串发送到 MySQL 服务器,并立即执行它。

        与 mysql_query() 函数相比,mysql_real_query() 提供了更多的灵活性,因为它允许你指定 SQL 语句的长度,这对于处理二进制数据或包含空字符的字符串特别有用。

函数原型

 int mysql_real_query(MYSQL *mysql, const char *query, unsigned long length); 

参数

  1. MYSQL *mysql: 这是一个指向已经初始化并成功连接到 MySQL 服务器的 MYSQL 对象的指针。
  2. const char *query: 这是一个指向包含要执行的 SQL 语句的字符串的指针。
  3. unsigned long length: 这是 query 字符串的长度(以字节为单位)。如果你传递的是以空字符结尾的字符串,你可以使用 strlen(query) 来获取这个长度;但是,如果你处理的是二进制数据或包含空字符的字符串,你需要显式地指定长度。

返回值

  1. 如果函数成功执行,则返回 0。
  2. 如果函数执行失败(例如,因为 SQL 语句无效或连接已关闭),则返回一个非零值。在这种情况下,你可以使用 mysql_error() 函数来获取更详细的错误信息。

3.3.使用示例

main.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <mysql/mysql.h>

const char* host = "localhost";
const char* user = "john";
const char* password = "123456";
const char* db = "db2";
u_int32_t port = 3306;

int main()
{
    // 1.初始化MYSQL对象
    MYSQL* my = mysql_init(NULL);
    if (!my)
    {
        perror("mysql_init fail: ");
        exit(errno);
    }

    // 2.连接MySQL服务器
    if (!mysql_real_connect(my, host, user, password, db, port, NULL, 0))
    {
        perror("mysql_real_connect fail: ");
        exit(errno);
    }

    // 3. 设置连接的编码格式
    if (0 != mysql_set_character_set(my, "utf8mb4"))
    {
        perror("mysql_set_character_set fail: ");
        exit(errno);
    }

    // 4.下达创建表的SQL指令
    const char* sql1 = "create table testA (id int,name varchar(10));";
    if (0 != mysql_query(my, sql1))
    {
        perror("mysql_query fail: ");
        exit(errno);
    }
    
    // 5.下达插入数据的SQL指令
    const char* sql2 = "insert into testA values(1,'A'),(2,'B'),(3,'C');";
    if (0 != mysql_query(my, sql2))
    {
        perror("mysql_query fail: ");
        exit(errno);
    }

    // 6.下达查询SQL指令
    const char* sql3 = "select * from testA";
    if (0 != mysql_query(my, sql3))
    {
        perror("mysql_query fail: ");
        exit(errno);
    }


    // 4. 关闭连接,释放对象
    mysql_close(my);
    
    return 0;
}

makefile 

test:main.c
    gcc -o $@ $^ -std=c99 -L /usr/lib/x86_64-linux-gnu -lmysqlclient
.PHONY:clean
    rm -rf test

 运行之后发现

很完美!!!!

如果想要知道mysql_real_query版本的可以看这里

#include <stdio.h>  
#include <errno.h>  
#include <unistd.h>  
#include <string.h> // 为了使用 strlen 函数  
#include <mysql/mysql.h>  
  
const char* host = "localhost";  
const char* user = "john";  
const char* password = "123456";  
const char* db = "db2";  
u_int32_t port = 3306;  
  
int main()  
{  
    // 1. 初始化MYSQL对象  
    MYSQL* my = mysql_init(NULL);  
    if (!my)  
    {  
        perror("mysql_init fail: ");  
        exit(errno);  
    }  
  
    // 2. 连接MySQL服务器  
    if (!mysql_real_connect(my, host, user, password, db, port, NULL, 0))  
    {  
        fprintf(stderr, "mysql_real_connect fail: %s\n", mysql_error(my));  
        mysql_close(my);  
        exit(errno);  
    }  
  
    // 3. 设置连接的编码格式  
    if (0 != mysql_set_character_set(my, "utf8mb4"))  
    {  
        fprintf(stderr, "mysql_set_character_set fail: %s\n", mysql_error(my));  
        mysql_close(my);  
        exit(errno);  
    }  
  
    // 4. 下达创建表的SQL指令  
    const char* sql1 = "create table testA (id int, name varchar(10));";  
    if (0 != mysql_real_query(my, sql1, strlen(sql1)))  
    {  
        fprintf(stderr, "mysql_real_query fail: %s\n", mysql_error(my));  
        mysql_close(my);  
        exit(errno);  
    }  
      
    // 5. 下达插入数据的SQL指令  
    const char* sql2 = "insert into testA values(1,'A'),(2,'B'),(3,'C');";  
    if (0 != mysql_real_query(my, sql2, strlen(sql2)))  
    {  
        fprintf(stderr, "mysql_real_query fail: %s\n", mysql_error(my));  
        mysql_close(my);  
        exit(errno);  
    }  
  
    // 6. 下达查询SQL指令  
    const char* sql3 = "select * from testA";  
    if (0 != mysql_real_query(my, sql3, strlen(sql3)))  
    {  
        fprintf(stderr, "mysql_real_query fail: %s\n", mysql_error(my));  
        mysql_close(my);  
        exit(errno);  
    }  
  
    // 注意:虽然您执行了查询,但您没有处理查询结果。  
    // 如果您需要处理结果,请使用 mysql_store_result 或 mysql_use_result。  
  
    // 7. 关闭连接,释放对象  
    mysql_close(my);  
      
    return 0;  
}

4.MYSQL的几个获取查询结果的C API

当我们对数据库中的数据进行增删改操作时,我们只关心操作有没有成功。

而对数据库中的数据进行查询操作时,除了需要知道有没有成功,还需要获取查询结果。

获取查询结果的函数如下:

4.1.mysql_store_result()

mysql_store_result() 函数是 MySQL C API 中用于处理查询结果集的一个重要函数。它的主要作用是将从 MySQL 服务器检索到的全部结果集读取到客户端,并将这些数据存储在一个 MYSQL_RES 结构中,以便后续进行行数据的提取和处理。

以下是关于 mysql_store_result() 函数的详细解释和使用指南:

函数原型

MYSQL_RES *mysql_store_result(MYSQL *mysql); 

参数

  • MYSQL *mysql:这是一个指向已经成功连接到 MySQL 服务器的 MYSQL 结构体的指针。

返回值

  1. 成功时,返回一个指向 MYSQL_RES 结构体的指针,该结构体包含了查询的结果集。
  2. 如果查询没有结果集(例如,执行的是 INSERT、UPDATE 等非查询语句),则返回 NULL。
  3. 如果读取结果集失败,也会返回 NULL。此时,可以通过 mysql_error() 和 mysql_errno() 函数来检查具体的错误信息。

使用场景

  1. 当你执行了一个需要返回结果集的查询(如 SELECT、SHOW、DESCRIBE 等)后,应调用 mysql_store_result() 来获取结果集。
  2. 对于不需要返回结果集的查询(如 INSERT、UPDATE、DELETE 等),则无需调用此函数。

注意事项

  1. 调用 mysql_store_result() 后,必须检查返回值是否为 NULL,以区分是无结果集还是发生了错误。
  2. 可以通过 mysql_field_count() 函数来检查一个查询是否应该返回结果集,这有助于在调用 mysql_store_result() 前进行预处理。
  3. 在处理完结果集后,必须调用 mysql_free_result() 来释放 MYSQL_RES 结构体占用的内存,以避免内存泄漏。
  4. mysql_store_result() 会将结果集的所有数据从服务器读取到客户端内存中,因此,对于非常大的结果集,可能会导致客户端内存不足的问题。此时,可以考虑使用 mysql_use_result() 函数,它逐行读取结果集,不会一次性加载到内存中。

常见错误代码

  1. CR_COMMANDS_OUT_OF_SYNC:命令执行顺序不当。
  2. CR_OUT_OF_MEMORY:内存溢出。
  3. CR_SERVER_GONE_ERROR:MySQL 服务器不可用。
  4. CR_SERVER_LOST:在查询过程中与服务器的连接丢失。
  5. CR_UNKNOWN_ERROR:出现未知错误。

4.2.mysql_use_result()

MYSQL_RES *mysql_use_result(MYSQL *mysql)

参数

  • MYSQL *mysql:这是一个指向已经成功连接到 MySQL 服务器的 MYSQL 结构体的指针。

mysql_use_result() 是 MySQL C API 中的一个函数,用于从 MySQL 服务器检索查询结果集,特别是用于 SELECT、SHOW、DESCRIBE 和 EXPLAIN 等查询。与 mysql_store_result() 不同,mysql_use_result() 不会一次性将结果集全部读取到客户端,而是逐行读取,这样可以减少内存使用并提高速度,特别是在处理大量数据时。

主要特点和使用场景

  1. 逐行读取:mysql_use_result() 允许你通过 mysql_fetch_row() 逐行获取数据,而不是像 mysql_store_result() 那样一次性将所有数据加载到客户端内存中。
  2. 内存效率:由于逐行读取,它使用的内存更少,适合处理大数据集。
  3. 速度:对于非常大的数据集,mysql_use_result() 可能比 mysql_store_result() 更快,因为它减少了数据传输和内存分配的开销。
  4. 阻塞服务器:使用 mysql_use_result() 时,MySQL 服务器会一直忙于发送结果集,直到所有行都被读取或客户端断开连接。这可能会阻止其他客户端更新用于查询的表。

使用注意事项

  1. 必须逐行读取:必须调用 mysql_fetch_row() 直到返回 NULL,否则未读取的行会在下次查询时返回。
  2. 避免使用特定函数:在 mysql_use_result() 返回的结果集上,不应使用 mysql_data_seek()、mysql_row_seek()、mysql_row_tell()、mysql_num_rows() 或 mysql_affected_rows()。
  3. 完成读取后释放:完成结果集处理后,必须使用 mysql_free_result() 释放资源。
  4. 错误处理:如果 mysql_use_result() 返回 NULL,表示发生错误,可以通过 mysql_error() 和 mysql_errno() 获取错误信息。

错误代码

  1. CR_COMMANDS_OUT_OF_SYNC:命令执行顺序不当。
  2. CR_OUT_OF_MEMORY:内存不足。
  3. CR_SERVER_GONE_ERROR:MySQL 服务器不可用。
  4. CR_SERVER_LOST:在查询过程中与服务器的连接丢失。
  5. CR_UNKNOWN_ERROR:发生未知错误。

适用场景

  1. 当处理非常大的数据集,且服务器内存可能不足时。
  2. 当客户端不需要一次性处理所有数据,可以逐行处理时。
  3. 当不希望阻塞服务器进行其他操作时,应避免使用 mysql_use_result()。

4.4.mysql_store_result()和mysql_use_result()的区别

 两者的主要区别是。

  • mysql_use_result() 的结果必须“一次性用完”,也就是说用它得到一个 result 后,必须反复用 mysql_fetch_row() 读取其结果直至该函数返回 null 为止,否则如果你再次进行 mysql 查询,会得到 “Commands out of sync; you can't run this command now” 的错误。
  • 而 mysql_store_result() 得到 result 是存下来的,你无需把全部行结果读完,就可以进行另外的查询。比如你进行一个查询,得到一系列记录,再根据这些结果,用一个循环再进行数据库查询,就只能用 mysql_store_result() 。 

接下来我们看它们的优缺点!

mysql_store_result()

  • 行为:该函数从服务器一次性检索整个结果集,并将其存储在客户端内存中。
  • 优点
    • 可以随机访问结果集中的任何行(使用mysql_data_seek()或mysql_row_seek())。
    • 可以在读取结果集之前或期间进行其他查询,因为结果集已被完全加载到客户端。
    • 可以使用mysql_num_rows()获取结果集中的行数。
  • 缺点
    • 对于大型结果集,可能需要大量内存,可能导致内存溢出。

mysql_use_result()

  • 行为:该函数初始化逐行检索结果集的过程,但不立即从服务器获取任何行。行是通过后续的mysql_fetch_row()调用逐个检索的。
  • 优点
    • 客户端所需的内存较少,因为一次只处理一行。
    • 对于服务器来说,处理大型结果集时的内存需求也较低,因为行是按需发送的。
  • 缺点
    • 必须连续处理每一行,不能随机访问结果集中的行。
    • 在开始检索结果集之前,不能执行其他查询,否则会出现“Commands out of sync”错误。
    • 无法在检索所有行之前使用mysql_num_rows()获取行数。
    • 如果在检索过程中发生错误,mysql_fetch_row()可能返回NULL,需要通过mysql_errno()和mysql_error()来确定是到达结果集末端还是发生错误。

选择哪个函数?

  1. 如果你的应用程序需要随机访问结果集中的行,或者需要知道结果集中的行数,那么mysql_store_result()是更好的选择。但是,请确保你的客户端有足够的内存来处理可能的大型结果集。
  2. 如果你的应用程序可以逐行处理结果集,并且希望减少客户端和服务器之间的内存使用,那么mysql_use_result()可能更适合你。但是,请注意,你必须连续处理每一行,以避免妨碍服务器的其他操作。

注意事项

  1. 无论使用哪个函数,一旦完成对结果集的操作,都应该调用mysql_free_result()来释放结果集使用的内存。
  2. 在调用mysql_store_result()或mysql_use_result()之前,应该使用mysql_field_count()来检查查询是否应该返回结果集(例如,INSERT语句不会返回结果集)。
  3. 如果在调用mysql_store_result()或mysql_use_result()时遇到错误,应该检查mysql_error()和mysql_errno()来获取更多信息。

实际上,客户端最常使用的是 mysql_store_result() 。 


4.3.获取查询结果的行数——mysql_num_rows() 

my_ulonglong mysql_num_rows(MYSQL_RES *result) 
  • 返回结果集中的行数。 

事实上mysql_num_rows() 函数的行为依赖于你是使用 mysql_store_result() 还是 mysql_use_result() 来获取结果集。

使用 mysql_store_result() 时

  • 当你使用 mysql_store_result() 时,MySQL 客户端库会将查询结果集的所有行从服务器读取到客户端,并存储在一个 MYSQL_RES 结构中。因此,一旦 mysql_store_result() 成功返回了一个非 NULL 的 MYSQL_RES 指针,你就可以立即调用 mysql_num_rows() 来获取结果集中的行数。此时,mysql_num_rows() 将返回一个准确的行数,因为所有行都已经被读取并存储在客户端了。

使用 mysql_use_result() 时

  • 相比之下,当你使用 mysql_use_result() 时,MySQL 客户端库会逐行从服务器读取结果集,而不是一次性读取所有行。这意味着在调用 mysql_use_result() 后,你必须使用 mysql_fetch_row() 或其他类似的函数来逐行检索结果集。在这种情况下,如果你试图在检索所有行之前调用 mysql_num_rows(),它将无法返回正确的行数,因为库还没有读取到所有的行。
  • 实际上,在使用 mysql_use_result() 时,调用 mysql_num_rows() 可能是没有意义的,因为该函数的目的是要知道结果集中有多少行,但如果你正在逐行处理结果集,你通常不需要这个信息,或者你可以在遍历完所有行后自己计数。

总结

  • 如果你使用 mysql_store_result(),你可以立即调用 mysql_num_rows() 来获取行数。
  • 如果你使用 mysql_use_result(),你应该避免在检索所有行之前调用 mysql_num_rows(),因为此时它不会返回正确的值。

4.4.获取查询结果的列数——mysql_num_fields()

unsigned int mysql_num_fields(MYSQL_RES *result)

描述:

  • 返回结果集中的列数(字段数)。这个函数应该在使用 mysql_store_result() 或 mysql_use_result() 成功获取结果集后调用,并且结果集指针 result 不应为 NULL。

参数:

  • MYSQL_RES *result:指向结果集的指针,该结果集是通过 mysql_store_result() 或 mysql_use_result() 获得的。

返回值:

  • 返回一个无符号整数,表示结果集中的列数。

错误:

  • 如果 result 为 NULL,则行为未定义(通常会导致程序崩溃或未定义行为,因为试图访问空指针)。

4.5.获取查询结果的列属性——mysql_fetch_fields()

MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)

参数

  • MYSQL_RES *result:指向结果集的指针,该结果集是通过 mysql_store_result() 或 mysql_use_result() 获得的。

描述

  1. mysql_fetch_field() 函数用于从结果集中检索列的信息,这些信息以 MYSQL_FIELD 结构体的形式返回。每次调用该函数时,它会返回结果集中的下一列的信息。当结果集中没有剩余的列时,该函数返回 NULL。
  2. 在执行新的 SELECT 查询后,mysql_fetch_field() 会被重置,以便从第一个字段的信息开始返回。此外,调用 mysql_field_seek() 函数也会影响 mysql_fetch_field() 返回的字段,允许用户跳转到结果集中的特定字段。
  3. 需要注意的是,如果在执行 SELECT 查询后调用了 mysql_query() 但未调用 mysql_store_result(),此时调用 mysql_fetch_field() 来请求 BLOB 字段的长度时,MySQL 将返回默认的 BLOB 长度(8KB),因为此时 MySQL 不知道 BLOB 的最大长度。一旦结果集被检索(即调用了 mysql_store_result()),field->max_length 将包含特定查询中该列的最大值的实际长度。

返回值

  • 返回一个指向 MYSQL_FIELD 结构体的指针,该结构体包含当前列的详细信息。
  • 如果结果集中没有剩余的列,则返回 NULL。

错误处理

  • 该函数不返回错误码。如果 result 指针无效或未正确指向一个结果集,则行为未定义(通常会导致程序崩溃或未定义行为)。

MYSQL_FIELD成员

        我们发现MYSQL和MYSQL_RES里面都有MYSQL_FIELD结构体。那么这个结构体有什么含义呢?

        MYSQL_FIELD中包含了字段名、字段类型和大小等信息。可以重复调用mysql_fetch_field函数获得所有字段的信息。可以通过重复调用mysql_fetch_field()对每一列获得MYSQL_FIELD结构。字段值不是这个结构的部分;它们被包含在一个MYSQL_ROW结构中。

typedef struct st_mysql_field {
  char *name;                 /* Name of column */
  char *org_name;             /* Original column name, if an alias */
  char *table;                /* Table of column if column was a field */
  char *org_table;            /* Org table name, if table was an alias */
  char *db;                   /* Database for table */
  char *catalog;          /* Catalog for table */
  char *def;                  /* Default value (set by mysql_list_fields) */
  unsigned long length;       /* Width of column (create length) */
  unsigned long max_length;   /* Max width for selected set */
  unsigned int name_length;
  unsigned int org_name_length;
  unsigned int table_length;
  unsigned int org_table_length;
  unsigned int db_length;
  unsigned int catalog_length;
  unsigned int def_length;
  unsigned int flags;         /* Div flags */
  unsigned int decimals;      /* Number of decimals in field */
  unsigned int charsetnr;     /* Character set */
  enum enum_field_types type; /* Type of field. See mysql_com.h for types */
  void *extension;
} MYSQL_FIELD;

个结构体在 MySQL C API 中用于描述查询结果集中的字段信息。以下是对该结构体中各个成员的详细解释:

  1. char *name;:指向字段名的字符串指针。
  2. char *org_name;:如果字段使用了别名,则指向原始字段名的字符串指针。
  3. char *table;:如果字段是表中的一个列,则指向表名的字符串指针。
  4. char *org_table;:如果表使用了别名,则指向原始表名的字符串指针。
  5. char *db;:指向数据库名的字符串指针,该数据库包含字段所在的表。
  6. char *catalog;:指向目录名的字符串指针(在 MySQL 中通常不使用)。
  7. char *def;:指向字段默认值的字符串指针(注意,这个默认值可能是通过 mysql_list_fields 函数设置的,而不是直接从数据库元数据中获取的)。
  8. unsigned long length;:字段的宽度(在创建表时指定的宽度)。
  9. unsigned long max_length;:对于选定的数据集,字段的最大宽度。
  10. unsigned int name_length, org_name_length, table_length, org_table_length, db_length, catalog_length, def_length;:分别表示字段名、原始字段名、表名、原始表名、数据库名、目录名和默认值的长度。
  11. unsigned int flags;:字段标志,用于指示字段的额外属性(如是否允许 NULL、是否为主键等)。这些标志通常是多个位组合而成的,每个位代表一个不同的属性。
  12. unsigned int decimals;:字段中的小数位数。
  13. unsigned int charsetnr;:字段使用的字符集编号。
  14. enum enum_field_types type;:字段的类型。这是一个枚举类型,包含了 MySQL 支持的所有字段类型(如 MYSQL_TYPE_INT, MYSQL_TYPE_VARCHAR 等)。
  15. void *extension;:指向扩展数据的指针,可能用于将来的扩展或特定于实现的用途。

        当您使用 MySQL C API 执行查询并获取结果集时,可以调用 mysql_fetch_field() 函数来遍历结果集中的所有字段,并获取每个字段的 MYSQL_FIELD 结构体。这个函数每次调用都会返回结果集中下一个字段的 MYSQL_FIELD 结构体指针,直到返回 NULL 表示没有更多字段。

        字段的实际值并不包含在 MYSQL_FIELD 结构体中,而是存储在 MYSQL_ROW 结构体的数组中。MYSQL_ROW 是一个字符指针数组,每个指针都指向一行中一个字段的值。您可以通过调用 mysql_fetch_row() 函数来获取结果集中的每一行,并遍历 MYSQL_ROW 数组来访问每个字段的值。

一个简单的使用例子如下 

 // 定义一个 MYSQL_FIELD 类型的指针,用于指向当前正在处理的字段  
MYSQL_FIELD *field;  
  
// 使用 while 循环遍历结果集中的每一列  
// mysql_fetch_field() 函数每次调用都会返回结果集中的下一个字段的信息,直到没有更多字段时返回 NULL  
while ((field = mysql_fetch_field(res)) != NULL) {  
    // 打印字段的名称  
    printf("Field Name: %s\n", field->name);  
      
    // 打印字段的类型,这是一个整数,代表 MySQL 中的数据类型(如 MYSQL_TYPE_INT, MYSQL_TYPE_VARCHAR 等)  
    printf("Field Type: %d\n", field->type);  
      
    // 检查字段是否标记为 NOT NULL  
    // 如果字段的 flags 属性中包含 NOT_NULL_FLAG,则表示该字段不能为 NULL  
    if (field->flags & NOT_NULL_FLAG)  
        printf("Field is NOT NULL\n");  
    else  
        printf("Field can be NULL\n");  
      
    // 打印字段的键类型  
    // key 是一个整数,表示字段在索引中的位置(如果有的话),0 表示不是键  
    printf("Field Key: %d\n", field->key);  
      
    // 打印字段的最大长度  
    // 对于字符串类型,这是字段可以存储的最大字符数;对于其他类型,这个值可能没有意义  
    printf("Field Max Length: %lu\n", field->max_length);  
      
      
    // 打印字段的小数位数  
    // 对于数值类型字段(如 DECIMAL),这表示字段中小数点后的位数  
    printf("Field Decimals: %d\n", field->decimals);  
      
    // 打印字段的默认值  
    // 如果字段有默认值,这里将打印出来;否则,打印的是 NULL 或空字符串(取决于 MySQL 的版本和字段定义)  
    // 请注意,对于某些数据类型(如 BLOB、TEXT),默认值可能不会被存储或返回  
    printf("Field Default Value: %s\n", field->def);  
      
    // 打印一个空行,以便于区分不同的字段信息  
    printf("\n");  
}

 


4.6.获取查询结果中的一行数据——mysql_fetch_rows()

获取查询结果中的一行数据的函数如下:

MYSQL_ROW
mysql_fetch_row(MYSQL_RES *result)
  • mysql_fetch_row() 函数用于从结果集中检索下一行数据。

使用场景

  1. 在 mysql_store_result() 之后使用:如果没有更多行可以检索,mysql_fetch_row() 返回 NULL。
  2. 在 mysql_use_result() 之后使用:如果没有更多行可以检索或发生了错误,mysql_fetch_row() 返回 NULL。

行数据访问

  1. 行中值的个数由 mysql_num_fields(result) 给出。
  2. 调用 mysql_fetch_row() 返回的 MYSQL_ROW 是一个指向行中值的指针数组。
  3. 可以通过 row[0] 到 row[mysql_num_fields(result)-1] 访问这些值。
  4. 行中的 NULL 值由 NULL 指针指示。

字段值长度

  1. 可以通过调用 mysql_fetch_lengths() 获取行中字段值的长度。
  2. 对于空字段和包含 NULL 的字段,长度为 0。
  3. 通过检查字段值的指针可以区分它们:如果指针为 NULL,则字段为 NULL;否则,字段为空字符串。

返回值

  • 返回值为 MYSQL_ROW 类型的结构,指向下一行的数据。
  • 如果没有更多行可以检索或发生了错误(在 mysql_use_result() 模式下),返回 NULL。

错误处理

  1. 在两次调用 mysql_fetch_row() 之间,错误状态不会被重置。
  2. 如果在 mysql_use_result() 模式下返回 NULL,要确定是否发生错误,可以检查 mysql_error() 是否返回非空字符串或 mysql_errno() 是否返回非零值。

常见错误代码

  1. CR_SERVER_LOST:在查询过程中,与服务器的连接丢失。
  2. CR_UNKNOWN_ERROR:发生未知错误。

        MYSQL_ROW成员

MYSQL_ROW:这是一个行数据的类型安全(type-safe)的表示。当前它实现为一个计数字节的字符串数组。(如果字段值可能包含二进制数据,你不能将这些视为空终止串,因为这样的值可以在内部包含空字节) 行通过调用mysql_fetch_row()获得。

typedef char **MYSQL_ROW;

MySQL C API 中,MYSQL_ROW 是一个用于表示从数据库查询结果集中检索到的单行数据的类型。它是一个指向字符指针数组的指针,其中每个字符指针都指向一个字段的值。


4.7.释放结果集——mysql_free_result()

void mysql_free_result(MYSQL_RES *result)

描述

释放由mysql_store_result()、mysql_use_result()、mysql_list_dbs()等为结果集分配的内存。完成对结果集的操作后,必须调用mysql_free_result()释放结果集使用的内存。

释放完成后,不要尝试访问结果集。

返回值

无。

错误

无。


4.8.使用示例

main.c

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <mysql/mysql.h>

const char* host = "localhost";
const char* user = "john";
const char* password = "123456";
const char* db = "db2";
u_int32_t port = 3306;

int main()
{
    // 1.初始化MYSQL对象
    MYSQL* my = mysql_init(NULL);
    if (!my)
    {
        perror("mysql_init fail: ");
        exit(errno);
    }

    // 2.连接MySQL服务器
    if (!mysql_real_connect(my, host, user, password, db, port, NULL, 0))
    {
        perror("mysql_real_connect fail: ");
        exit(errno);
    }

    // 3. 设置连接的编码格式
    if (0 != mysql_set_character_set(my, "utf8mb4"))
    {
        perror("mysql_set_character_set fail: ");
        exit(errno);
    }

    // 4.下达查询SQL指令
    const char* sql = "select * from testA";
    if (0 != mysql_query(my, sql))
    {
        perror("mysql_query fail: ");
        exit(errno);
    }

     // 5.获得存储结果
    MYSQL_RES* my_res = mysql_store_result(my);
    // 5.1获得结果集中的行数
    my_ulonglong rows = mysql_num_rows(my_res);
    // 5.2获得结果集中的列数
    u_int32_t column = mysql_num_fields(my_res);
    // 5.3获得结果集中的字段属性
    MYSQL_FIELD* filed_array = mysql_fetch_fields(my_res);

    // 5.4获取结果集中的数据,并进行打印
    // 打印字段的属性
    for (int i = 0; i < column; i++)
    {
        printf("%s\t", filed_array[i].name);
    }
    printf("\n");
    printf("----------------------------\n");

    // 打印字段的数据
    for (int i = 0; i < rows; i++)
    {
        MYSQL_ROW row_data = mysql_fetch_row(my_res);
        for (int j = 0; j < column; j++)
        {
            printf("%s\t", row_data[j]);
        }
        printf("\n");
    }

    // 6.释放结果集
    mysql_free_result(my_res);


    // 4. 关闭连接,释放对象
    mysql_close(my);
    
    return 0;
}

makefile 

test:main.c
    gcc -o $@ $^ -std=c99 -L /usr/lib/x86_64-linux-gnu -lmysqlclient
.PHONY:clean
    rm -rf test

 运行之后发现

很完美!!



http://www.kler.cn/news/367884.html

相关文章:

  • hcia复习篇
  • 基于丑萌气质狗--C#的sqlserver学习
  • Linux复习-C++
  • Next.js、Prisma 和 MySQL 实践示例
  • Redis 哨兵 总结
  • C#判断带数字的字符串数组连续性的两种方式
  • 手把手教——class1_VScode配置C++环境
  • 大粤金融智能交易系统的创新与应用
  • FPGA 蜂鸣器 音乐播放器
  • 【Docker命令】日常使用的Docker命令
  • Pandas库学习Day21
  • javaWeb项目-ssm+vue高校网课管理系统功能介绍
  • Cursor零基础小白教程系列 - 创建你的第一个Cursor 项目
  • CSS伪元素以及伪类和CSS特性
  • 获 Sei 基金会投资的 MetaArena :掀起新一轮链上游戏革命
  • Adam优化器算法详解
  • 【C++复习】第二弹-内存管理
  • 3.Linux按键驱动-添加循环队列
  • 【Android】多渠道打包配置
  • Android 自定义 Dialog 实现列表 单选,多选,搜索
  • Python4
  • 大学新生如何入门编程:选择语言、制定计划及避开学习陷阱
  • Page Cache(页缓存
  • 学习记录:js算法(七十五): 加油站
  • 【C++】异常处理实例详解
  • 探索Spring Boot:构建高性能论坛网站