ZetCode

MySQL C API 编程

最后修改于 2020 年 9 月 23 日

这是一个针对 MySQL 数据库的 C 语言编程教程。它涵盖了使用 C API 进行 MySQL 编程的基础知识。您也可以考虑查看 ZetCode 上的 MySQL 教程

关于 MySQL 数据库

MySQL 是一款领先的开源数据库管理系统。它是一个多用户、多线程的数据库管理系统。MySQL 在 Web 领域尤其流行。MySQL 目前归 Oracle 公司所有。MySQL 数据库可在大多数主流操作系统平台上使用。它可以在 BSD Unix、Linux、Windows 或 Mac OS 上运行。

MariaDB 是一个由社区开发、有商业支持的 MySQL 关系型数据库管理系统的分支。

$ sudo apt install default-libmysqlclient-dev

为了能够编译 C 语言示例,我们需要安装 MySQL C 开发库。上面的命令行展示了如何在基于 Debian 的 Linux 系统上进行安装。

C99

本教程使用 C99 标准。对于 GNU C 编译器,我们需要添加 -std=c99 选项。对于 Windows 用户,强烈推荐使用 Pelles C IDE。(MSVC 不支持 C99。)

MYSQL *con = mysql_init(NULL);

在 C99 中,我们可以将声明与代码混合在一起。在旧的 C 程序中,我们需要将这行代码分成两行。

MySQL C 第一个示例

在第一个示例中,我们测试一个 MySQL 函数的调用。

version.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  printf("MySQL client version: %s\n", mysql_get_client_info());

  exit(0);
}

mysql_get_client_info 函数显示 MySQL 客户端的版本。

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

我们包含了必要的头文件。

printf("MySQL client version: %s\n", mysql_get_client_info());

这行代码输出了 MySQL 客户端的版本。为此,我们使用了 mysql_get_client_info 函数调用。

exit(0);

我们从脚本中退出。

$ c99 version.c -o version  `mysql_config --cflags --libs`

这是我们编译代码示例的方式。

$ ./version
MySQL client version: 10.3.24

这是输出。

MySQL C 创建数据库

下一个代码示例将创建一个数据库。该代码示例可以分为以下几个部分:

createdb.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "%s\n", mysql_error(con));
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "root", "root_passwd",
          NULL, 0, NULL, 0) == NULL)
  {
      fprintf(stderr, "%s\n", mysql_error(con));
      mysql_close(con);
      exit(1);
  }

  if (mysql_query(con, "CREATE DATABASE testdb"))
  {
      fprintf(stderr, "%s\n", mysql_error(con));
      mysql_close(con);
      exit(1);
  }

  mysql_close(con);
  exit(0);
}

该代码示例连接到 MySQL 数据库系统并创建一个名为 testdb 的新数据库。

MYSQL *con = mysql_init(NULL);

mysql_init 函数分配或初始化一个适用于 mysql_real_connect 函数的 MYSQL 对象。请记住,这里是 C99 标准。

if (con == NULL)
{
    fprintf(stderr, "%s\n", mysql_error(con));
    exit(1);
}

我们检查返回值。如果 mysql_init 函数失败,我们打印错误消息并终止应用程序。

if (mysql_real_connect(con, "localhost", "root", "root_passwd",
    NULL, 0, NULL, 0) == NULL)
{
    fprintf(stderr, "%s\n", mysql_error(con));
    mysql_close(con);
    exit(1);
}

mysql_real_connect 函数建立与数据库的连接。我们向该函数提供连接句柄、主机名、用户名和密码参数。其他四个参数分别是数据库名、端口号、Unix 套接字和客户端标志。创建新数据库需要超级用户权限。

if (mysql_query(con, "CREATE DATABASE testdb"))
{
    fprintf(stderr, "%s\n", mysql_error(con));
    mysql_close(con);
    exit(1);
}

mysql_query 执行 SQL 语句。在我们的例子中,该语句创建了一个新的数据库。

mysql_close(con);

最后,我们关闭数据库连接。

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| testdb             |
+--------------------+
3 rows in set (0.00 sec)

这证明了数据库已成功创建。

MySQL C 创建并填充表

在创建新表之前,我们先创建一个将在本教程其余部分使用的用户。

mysql> CREATE USER user12@localhost IDENTIFIED BY '34klq*';

我们创建了一个新用户 user12

mysql> GRANT ALL ON testdb.* to user12@localhost;

这里我们将 testdb 数据库的所有权限授予 user12

下一个代码示例创建一个表并向其中插入一些数据。

create_populate.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "%s\n", mysql_error(con));
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "DROP TABLE IF EXISTS cars")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "CREATE TABLE cars(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), price INT)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(1,'Audi',52642)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(2,'Mercedes',57127)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(3,'Skoda',9000)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(4,'Volvo',29000)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(5,'Bentley',350000)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(6,'Citroen',21000)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(7,'Hummer',41400)")) {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO cars VALUES(8,'Volkswagen',21600)")) {
      finish_with_error(con);
  }

  mysql_close(con);
  exit(0);
}

这里我们没有使用任何新的 MySQL 函数调用。我们使用 mysql_query 函数调用来创建表和插入数据。

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

为了避免不必要的重复,我们创建了一个自定义的 finish_with_error 函数。

if (mysql_real_connect(con, "localhost", "user12", "34klq*",
        "testdb", 0, NULL, 0) == NULL)
{
    finish_with_error(con);
}

我们连接到 testdb 数据库。用户名是 user12,密码是 34klq*。第五个参数是数据库名。

if (mysql_query(con, "CREATE TABLE cars(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), price INT)")) {
    finish_with_error(con);
}

这里我们创建一个名为 cars 的表。它有三列。

if (mysql_query(con, "INSERT INTO cars VALUES(1,'Audi',52642)")) {
    finish_with_error(con);
}

我们向 cars 表中插入一行数据。

mysql> USE testdb;
mysql> SHOW TABLES;
+------------------+
| Tables_in_testdb |
+------------------+
| cars             |
+------------------+
1 row in set (0.00 sec)

我们显示数据库中的表。

mysql> SELECT * FROM cars;
+------+------------+--------+
| Id   | Name       | Price  |
+------+------------+--------+
|    1 | Audi       |  52642 |
|    2 | Mercedes   |  57127 |
|    3 | Skoda      |   9000 |
|    4 | Volvo      |  29000 |
|    5 | Bentley    | 350000 |
|    6 | Citroen    |  21000 |
|    7 | Hummer     |  41400 |
|    8 | Volkswagen |  21600 |
+------+------------+--------+
8 rows in set (0.00 sec)

我们从表中查询所有数据。

MySQL C 检索数据

在下一个示例中,我们从表中检索数据。

我们需要执行以下步骤:

retrieva_data.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "SELECT * FROM cars"))
  {
      finish_with_error(con);
  }

  MYSQL_RES *result = mysql_store_result(con);

  if (result == NULL)
  {
      finish_with_error(con);
  }

  int num_fields = mysql_num_fields(result);

  MYSQL_ROW row;

  while ((row = mysql_fetch_row(result)))
  {
      for(int i = 0; i < num_fields; i++)
      {
          printf("%s ", row[i] ? row[i] : "NULL");
      }

      printf("\n");
  }

  mysql_free_result(result);
  mysql_close(con);

  exit(0);
}

该示例显示了 cars 表中的所有行。

if (mysql_query(con, "SELECT * FROM cars"))
{
    finish_with_error(con);
}

我们执行查询以检索 cars 表中的所有数据。

MYSQL_RES *result = mysql_store_result(con);

我们使用 mysql_store_result 函数获取结果集。MYSQL_RES 是一个用于保存结果集的结构体。

int num_fields = mysql_num_fields(result);

我们获取表中的字段(列)数。

MYSQL_ROW row;

while ((row = mysql_fetch_row(result)))
{
    for(int i = 0; i < num_fields; i++)
    {
        printf("%s ", row[i] ? row[i] : "NULL");
    }

    printf("\n");
}

我们抓取行并将其打印到屏幕上。

mysql_free_result(result);
mysql_close(con);

我们释放资源。

$ ./retrieva_data
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

这是输出。

MySQL C 获取最后插入行的 ID

有时,我们需要确定最后插入行的 ID。我们可以通过调用 mysql_insert_id 函数来确定最后插入行的 ID。该函数仅在表中定义了 AUTO_INCREMENT 列时才有效。

last_row_id.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "DROP TABLE IF EXISTS writers"))
  {
      finish_with_error(con);
  }

  char *sql = "CREATE TABLE writers(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))";

  if (mysql_query(con, sql))
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO writers(name) VALUES('Leo Tolstoy')"))
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO writers(name) VALUES('Jack London')"))
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "INSERT INTO writers(name) VALUES('Honore de Balzac')"))
  {
      finish_with_error(con);
  }

  int id = mysql_insert_id(con);

  printf("The last inserted row id is: %d\n", id);

  mysql_close(con);
  exit(0);
}

创建了一个新表。三行数据被插入到表中。我们确定了最后插入行的 ID。

char *sql = "CREATE TABLE writers(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))";

id 列的类型是 AUTO_INCREMENT

int id = mysql_insert_id(con);

mysql_insert_id 函数返回由前一个 INSERTUPDATE 语句为 AUTO_INCREMENT 列生成的值。

$ ./last_row_id
The last inserted row id is: 3

这是输出。

MySQL C 获取列标题

在下一个示例中,我们从表中检索数据及其列名。

headers.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "SELECT * FROM cars LIMIT 3"))
  {
      finish_with_error(con);
  }

  MYSQL_RES *result = mysql_store_result(con);

  if (result == NULL)
  {
      finish_with_error(con);
  }

  int num_fields = mysql_num_fields(result);

  MYSQL_ROW row;
  MYSQL_FIELD *field;

  while ((row = mysql_fetch_row(result)))
  {
      for(int i = 0; i < num_fields; i++)
      {
          if (i == 0)
          {
             while(field = mysql_fetch_field(result))
             {
                printf("%s ", field->name);
             }

             printf("\n");
          }

          printf("%s  ", row[i] ? row[i] : "NULL");
      }
  }

  printf("\n");

  mysql_free_result(result);
  mysql_close(con);

  exit(0);
}

我们打印 cars 表的前三行数据。我们还包括了列标题。

MYSQL_FIELD *field;

MYSQL_FIELD 结构体包含有关字段的信息,例如字段的名称、类型和大小。字段的值不属于此结构体的一部分;它们包含在 MYSQL_ROW 结构体中。

if (i == 0)
{
    while(field = mysql_fetch_field(result))
    {
        printf("%s ", field->name);
    }

    printf("\n");
}

第一行包含了列标题。mysql_fetch_field 调用返回一个 MYSQL_FIELD 结构体。我们从这个结构体中获取列标题名称。

$ ./headers
id name price
1  Audi  52642
2  Mercedes  57127
3  Skoda  9000

这是我们程序的输出。

MySQL C 多条语句

可以在一次查询中执行多条 SQL 语句。我们必须在连接方法中设置 CLIENT_MULTI_STATEMENTS 标志。

multiple_statements.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  int status = 0;

  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, CLIENT_MULTI_STATEMENTS) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "SELECT name FROM cars WHERE id=2;\
      SELECT name FROM cars WHERE id=3;SELECT name FROM cars WHERE id=6"))
  {
      finish_with_error(con);
  }

  do {
      MYSQL_RES *result = mysql_store_result(con);

      if (result == NULL)
      {
          finish_with_error(con);
      }

      MYSQL_ROW row = mysql_fetch_row(result);

      printf("%s\n", row[0]);

      mysql_free_result(result);

      status = mysql_next_result(con);

      if (status > 0) {
          finish_with_error(con);
      }

  } while(status == 0);

  mysql_close(con);
  exit(0);
}

在这个示例中,我们在一次查询中执行了三条 SELECT 语句。

if (mysql_real_connect(con, "localhost", "user12", "34klq*",
        "testdb", 0, NULL, CLIENT_MULTI_STATEMENTS) == NULL)
{
    finish_with_error(con);
}

mysql_real_connect 方法的最后一个选项是客户端标志。它用于启用某些功能。CLIENT_MULTI_STATEMENTS 启用多条语句的执行。默认情况下这是禁用的。

if (mysql_query(con, "SELECT name FROM cars WHERE id=2;\
    SELECT name FROM cars WHERE id=3;SELECT name FROM cars WHERE id=6"))
{
    finish_with_error(con);
}

查询由三条 SELECT 语句组成。它们由分号 ; 字符分隔。反斜杠字符 \ 用于将字符串分成两行。它与多条语句无关。

do {
...
} while(status == 0);

代码被放置在 do/while 语句之间。数据检索需要分多个周期完成。我们分别为每个 SELECT 语句检索数据。

status = mysql_next_result(con);

我们期望有多个结果集。因此,我们调用 mysql_next_result 函数。它读取下一个语句的结果并返回一个状态以指示是否存在更多结果。如果执行成功并且还有更多结果,该函数返回 0。如果执行成功但没有更多结果,则返回 -1。最后,如果发生错误,它返回一个大于零的值。

if (status > 0) {
    finish_with_error(con);
}

我们检查错误。

$ ./multiple_statements
Mercedes
Skoda
Citroen

示例输出。

MySQL C 插入图片

有些人喜欢将图片放入数据库,有些人则喜欢将它们保存在文件系统中以供其应用程序使用。当我们处理大量图片时,会出现技术难题。图片是二进制数据。MySQL 数据库有一个特殊的数据类型来存储二进制数据,称为 BLOB(Binary Large Object,二进制大对象)。

mysql> CREATE TABLE images(id INT PRIMARY KEY, data MEDIUMBLOB);

在我们的示例中,我们创建了一个新的 Images 表。图片大小最大可达 16 MB。这是由 MEDIUMBLOB 数据类型决定的。

insert_image.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{

  FILE *fp = fopen("sid.jpg", "rb");

  if (fp == NULL)
  {
      fprintf(stderr, "cannot open image file\n");
      exit(1);
  }

  fseek(fp, 0, SEEK_END);

  if (ferror(fp)) {

      fprintf(stderr, "fseek() failed\n");
      int r = fclose(fp);

      if (r == EOF) {
          fprintf(stderr, "cannot close file handler\n");
      }

      exit(1);
  }

  int flen = ftell(fp);

  if (flen == -1) {

      perror("error occurred");
      int r = fclose(fp);

      if (r == EOF) {
          fprintf(stderr, "cannot close file handler\n");
      }

      exit(1);
  }

  fseek(fp, 0, SEEK_SET);

  if (ferror(fp)) {

      fprintf(stderr, "fseek() failed\n");
      int r = fclose(fp);

      if (r == EOF) {
          fprintf(stderr, "cannot close file handler\n");
      }

      exit(1);
  }

  char data[flen+1];

  int size = fread(data, 1, flen, fp);

  if (ferror(fp)) {

      fprintf(stderr, "fread() failed\n");
      int r = fclose(fp);

      if (r == EOF) {
          fprintf(stderr, "cannot close file handler\n");
      }

      exit(1);
  }

  int r = fclose(fp);

  if (r == EOF) {
      fprintf(stderr, "cannot close file handler\n");
  }

  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  char chunk[2*size+1];
  mysql_real_escape_string(con, chunk, data, size);

  char *st = "INSERT INTO images(id, data) VALUES(1, '%s')";
  size_t st_len = strlen(st);

  char query[st_len + 2*size+1];
  int len = snprintf(query, st_len + 2*size+1, st, chunk);

  if (mysql_real_query(con, query, len))
  {
      finish_with_error(con);
  }

  mysql_close(con);
  exit(0);
}

在这个示例中,我们向 images 表中插入一张图片。

#include <string.h>

这个 include 是为了使用 strlen 函数。

FILE *fp = fopen("woman.jpg", "rb");

if (fp == NULL)
{
    fprintf(stderr, "cannot open image file\n");
    exit(1);
}

这里我们打开图片文件。在当前工作目录中,我们应该有一个 sid.jpg 文件。

fseek(fp, 0, SEEK_END);

if (ferror(fp)) {

    fprintf(stderr, "fseek() failed\n");
    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "cannot close file handler\n");
    }

    exit(1);
}

我们使用 fseek 函数将文件指针移动到文件末尾。我们将要确定图片的大小。如果发生错误,错误指示器会被设置。我们使用 ferror 函数检查该指示器。如果发生错误,我们也会关闭已打开的文件句柄。

int flen = ftell(fp);

if (flen == -1) {

    perror("error occurred");
    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "cannot close file handler\n");
    }

    exit(1);
}

对于二进制流,ftell 函数返回从文件开头算起的字节数,即图片文件的大小。如果发生错误,该函数返回 -1 并设置 errno。perror 函数将 errno 的值解释为错误消息,并将其打印到标准错误输出流。

char data[flen+1];

在这个数组中,我们将存储图片数据。

int size = fread(data, 1, flen, fp);

我们从文件指针读取数据并将其存储在 data 数组中。函数返回成功读取的元素总数。

int r = fclose(fp);

if (r == EOF) {
    fprintf(stderr, "cannot close file handler\n");
}

数据读取后,我们可以关闭文件句柄。

char chunk[2*size+1];
mysql_real_escape_string(con, chunk, data, size);

mysql_real_escape_string 函数会在传入字符串中某些有潜在危险的字符前添加转义字符,即反斜杠 \。这有助于防止 SQL 注入攻击。新的缓冲区长度必须至少为 2*size+1

char *st = "INSERT INTO images(id, data) VALUES(1, '%s')";
size_t st_len = strlen(st);

这里我们开始构建 SQL 语句。我们使用 strlen 函数确定 SQL 字符串的大小。

char query[st_len + 2*size+1];
int len = snprintf(query, st_len + 2*size+1, st, chunk);

查询字符串必须足够长,以包含 SQL 语句字符串的大小和图片文件的大小。我们使用 snprintf 函数将格式化的输出写入查询缓冲区。

if (mysql_real_query(con, query, len))
{
    finish_with_error(con);
};

我们使用 mysql_real_query 函数执行查询。mysql_query 不能用于包含二进制数据的语句;我们必须改用 mysql_real_query

MySQL C 查询图片

在前面的示例中,我们向数据库中插入了一张图片。在接下来的示例中,我们从数据库中查询已插入的图片。

select_image.c
#include <mysql.h>
#include <stdio.h>
#include <stdlib.h>

void finish_with_error(MYSQL *con)
{
  fprintf(stderr, "%s\n", mysql_error(con));
  mysql_close(con);
  exit(1);
}

int main(int argc, char **argv)
{
  FILE *fp = fopen("sid2.jpg", "wb");

  if (fp == NULL)
  {
      fprintf(stderr, "cannot open image file\n");
      exit(1);
  }

  MYSQL *con = mysql_init(NULL);

  if (con == NULL)
  {
      fprintf(stderr, "mysql_init() failed\n");
      exit(1);
  }

  if (mysql_real_connect(con, "localhost", "user12", "34klq*",
          "testdb", 0, NULL, 0) == NULL)
  {
      finish_with_error(con);
  }

  if (mysql_query(con, "SELECT data FROM images WHERE id=1"))
  {
      finish_with_error(con);
  }

  MYSQL_RES *result = mysql_store_result(con);

  if (result == NULL)
  {
      finish_with_error(con);
  }

  MYSQL_ROW row = mysql_fetch_row(result);
  unsigned long *lengths = mysql_fetch_lengths(result);

  if (lengths == NULL) {
      finish_with_error(con);
  }

  fwrite(row[0], lengths[0], 1, fp);

  if (ferror(fp))
  {
      fprintf(stderr, "fwrite() failed\n");
      mysql_free_result(result);
      mysql_close(con);

      exit(1);
  }

  int r = fclose(fp);

  if (r == EOF) {
      fprintf(stderr, "cannot close file handler\n");
  }

  mysql_free_result(result);
  mysql_close(con);

  exit(0);
}

在这个示例中,我们从数据库数据创建一个图片文件。

FILE *fp = fopen("sid2.jpg", "wb");

if (fp == NULL)
{
    fprintf(stderr, "cannot open image file\n");
    exit(1);
}

我们打开一个新的文件句柄用于写入。

if (mysql_query(con, "SELECT data FROM images WHERE id=1"))
{
    finish_with_error(con);
}

我们从 images 表中选择 id 为 1 的行的 data 列。

MYSQL_ROW row = mysql_fetch_row(result);

行中包含原始数据。

unsigned long *lengths = mysql_fetch_lengths(result);

我们获取图片的长度。

fwrite(row[0], lengths[0], 1, fp);

if (ferror(fp))
{
    fprintf(stderr, "fwrite() failed\n");
    mysql_free_result(result);
    mysql_close(con);

    exit(1);
}

我们使用 fwrite 函数调用将检索到的数据写入磁盘。我们使用 ferror 函数检查错误指示器。

int r = fclose(fp);

if (r == EOF) {
    fprintf(stderr, "cannot close file handler\n");
}

写入图片数据后,我们使用 fclose 函数关闭文件句柄。

这是 MySQL C API 教程。您可能还对 ZetCode 上的 PyMySQL 教程MySQL Visual Basic 教程PHP mysqli 教程PostgreSQL C 教程SQLite C 教程感兴趣。