# MySQL C API 编程教程

> 原文： [http://zetcode.com/db/mysqlc/](http://zetcode.com/db/mysqlc/)

这是针对 MySQL 数据库的 C 编程教程。 它涵盖了使用 C API 进行 MySQL 编程的基础。 您还可以考虑查看有关 ZetCode 的 [MySQL 教程](/databases/mysqltutorial/)。

[Tweet](https://twitter.com/share) 

## 关于 MySQL 数据库

MySQL 是领先的开源数据库管理系统。 它是一个多用户，多线程的数据库管理系统。 MySQL 在网络上特别流行。 它是由 Linux，Apache，MySQL 和 PHP 组成的非常流行的 _LAMP_ 平台的一部分。 Oracle 当前拥有 MySQL。 MySQL 数据库在最重要的 OS 平台上可用。 它可以在 BSD Unix，Linux，Windows 或 Mac OS 上运行。 维基百科和 YouTube 使用 MySQL。 这些站点每天管理数百万个查询。 MySQL 有两个版本：MySQL 服务器系统和 MySQL 嵌入式系统。

```perl
$ sudo apt-get install libmysqlclient-dev

```

为了能够编译 C 示例，我们需要安装 MySQL C 开发库。 上一行显示了我们如何在基于 Debian 的 Linux 上做到这一点。

## C99

本教程使用 C99。 对于 GNU C 编译器，我们需要添加-std = c99 选项。 对于 Windows 用户，强烈建议使用 Pelles C IDE。 （MSVC 不支持 C99。）

```perl
MYSQL *con = mysql_init(NULL);

```

在 C99 中，我们可以将声明与代码混合使用。 在较早的 C 程序中，我们需要将这一行分成两行。

## 第一个例子

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

```perl
#include <my_global.h>
#include <mysql.h>

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

  exit(0);
}

```

`mysql_get_client_info()`显示 MySQL 客户端版本。

```perl
#include <my_global.h>
#include <mysql.h>

```

我们包括必要的头文件。 `mysql.h`是 MySQL 函数调用的最重要的头文件。 `my_global.h`包含一些全局函数声明。 除其他外，它包括标准输入/输出头文件。

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

```

此代码行输出 MySQL 客户端的版本。 为此，我们使用`mysql_get_client_info()`函数调用。

```perl
exit(0);

```

我们从脚本退​​出。

```perl
$ gcc version.c -o version  `mysql_config --cflags --libs`

```

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

```perl
$ ./version 
MySQL client version: 5.1.67

```

示例输出。

## 建立数据库

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

*   启动连接句柄结构
*   建立连接
*   执行查询
*   连接关闭

```perl
#include <my_global.h>
#include <mysql.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_pswd", 
          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`的新数据库。

```perl
MYSQL *con = mysql_init(NULL);

```

`mysql_init()`函数分配或初始化适合`mysql_real_connect()`函数的 MYSQL 对象。 记住这是 C99。

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

```

我们检查返回值。 如果`mysql_init()`功能失败，我们将打印错误消息并终止应用。

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

```

`mysql_real_connect()`功能建立与数据库的连接。 我们为该函数提供连接处理程序，主机名，用户名和密码参数。 其他四个参数是数据库名称，端口号，unix 套接字以及最后的客户端标志。 我们需要超级用户特权才能创建新数据库。

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

```

`mysql_query()`执行 SQL 语句。 在我们的例子中，该语句创建一个新数据库。

```perl
mysql_close(con);

```

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

```perl
$ gcc createdb.c -o createdb -std=c99  `mysql_config --cflags --libs`

```

第二个示例已经利用了 C99 标准的功能。 因此，我们需要添加`-std=c99`选项。

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

```

这是数据库已创建的证明。

## 创建并填充表

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

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

```

我们创建了一个新用户`user12`。

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

```

在这里，我们将所有特权授予`testdb`数据库上的`user12`。

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

```perl
#include <my_global.h>
#include <mysql.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, Name TEXT, 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()`函数调用来创建表并将数据插入其中。

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

```

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

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

```

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

```perl
if (mysql_query(con, "CREATE TABLE Cars(Id INT, Name TEXT, Price INT)")) {      
    finish_with_error(con);
}

```

在这里，我们创建一个名为`Cars`的表。 它具有三列。

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

```

我们在`Cars`表中插入一行。

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

```

我们显示数据库中的表。

```perl
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)

```

我们从表中选择所有数据。

## 从数据库中检索数据

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

我们需要执行以下步骤：

*   建立连接
*   执行查询
*   获取结果集
*   获取所有可用行
*   释放结果集

```perl
#include <my_global.h>
#include <mysql.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`表中的所有列。

```perl
if (mysql_query(con, "SELECT * FROM Cars")) 
{
    finish_with_error(con);
}

```

我们执行查询，该查询将从 Cars 表中检索所有数据。

```perl
MYSQL_RES *result = mysql_store_result(con);

```

我们使用`mysql_store_result()`函数获得结果集。 `MYSQL_RES`是用于保存结果集的结构。

```perl
int num_fields = mysql_num_fields(result);

```

我们获得表中的字段（列）数。

```perl
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"); 
}

```

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

```perl
mysql_free_result(result);
mysql_close(con);

```

我们释放资源。

```perl
$ ./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 

```

示例输出。

## 最后插入的行 ID

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

```perl
#include <my_global.h>
#include <mysql.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 TEXT)";

  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。

```perl
char *sql = "CREATE TABLE Writers(Id INT PRIMARY KEY AUTO_INCREMENT, Name TEXT)";

```

`Id`列具有`AUTO_INCREMENT`类型。

```perl
int id = mysql_insert_id(con);

```

`mysql_insert_id()`函数返回由先前的`INSERT`或`UPDATE`语句为`AUTO_INCREMENT`列生成的值。

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

```

输出。

## 列标题

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

```perl
#include <my_global.h>
#include <mysql.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`表中打印前三行。 我们还包括列标题。

```perl
MYSQL_FIELD *field;

```

`MYSQL_FIELD`结构包含有关字段的信息，例如字段的名称，类型和大小。 字段值不属于此结构； 它们包含在`MYSQL_ROW`结构中。

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

    printf("\n");           
}

```

第一行包含列标题。 `mysql_fetch_field()`调用返回`MYSQL_FIELD`结构。 我们从该结构中获取列标题名称。

```perl
$ ./headers 
Id Name Price 
1  Audi  52642  
2  Mercedes  57127  
3  Skoda  9000 

```

这是我们程序的输出。

## 多个语句

可以在一个查询中执行多个 SQL 语句。 我们必须在`connect`方法中设置`CLIENT_MULTI_STATEMENTS`标志。

```perl
#include <my_global.h>
#include <mysql.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`语句。

```perl
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`允许执行多个语句。 默认情况下禁用此功能。

```perl
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`语句。 它们之间用分号`;`字符分隔。 反斜杠字符`\`用于将字符串分成两行。 它与多个语句无关。

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

```

该代码位于`do/while`语句之间。 数据检索要分多个周期进行。 我们将分别为每个`SELECT`语句检索数据。

```perl
status = mysql_next_result(con); 

```

我们期望有多个结果集。 因此，我们称为`mysql_next_result()`函数。 它读取下一个语句结果，并返回状态以指示是否存在更多结果。 如果执行正常并且有更多结果，该函数将返回 0。 当执行 OK 且没有更多结果时，它将返回 -1。 最后，如果发生错误，它将返回大于零的值。

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

```

我们检查错误。

```perl
$ ./multiple_statements 
Mercedes
Skoda
Citroen

```

示例输出。

## 将图像插入 MySQL 数据库

有些人喜欢将其图像放入数据库中，有些人则希望将其保留在文件系统中以供其应用使用。 当我们处理大量图像时，会出现技术难题。 图像是二进制数据。 MySQL 数据库具有一种特殊的数据类型来存储称为`BLOB`（二进制大对象）的二进制数据。

```perl
mysql> CREATE TABLE Images(Id INT PRIMARY KEY, Data MEDIUMBLOB);

```

对于我们的示例，我们创建一个新的`Images`表。 图像大小最大为 16 MB。 它由`MEDIUMBLOB`数据类型确定。

```perl
#include <my_global.h>
#include <mysql.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("woman.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`表。

```perl
#include <string.h>

```

这包括`strlen()`功能。

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

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

```

在这里，我们打开图像文件。 在当前工作目录中，我们应该有`woman.jpg`文件。

```perl
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()`函数将文件指针移到文件末尾。 我们将确定图像的大小。 如果发生错误，则设置错误指示器。 我们使用`fseek()`功能检查指示器。 如果发生错误，我们还将关闭打开的文件处理程序。

```perl
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`。 `perrro()`函数将`errno`的值解释为错误消息，并将其打印到标准错误输出流。

```perl
char data[flen+1];

```

在这个数组中，我们将存储图像数据。

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

```

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

```perl
int r = fclose(fp);

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

```

读取数据后，我们可以关闭文件处理程序。

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

```

`mysql_real_escape_string()`函数在传递给该函数的字符串中的某些潜在危险字符之前添加转义字符反斜杠`\`。 这可以帮助防止 SQL 注入攻击。 新缓冲区的长度必须至少为`2*size+1`。

```perl
char *st = "INSERT INTO Images(Id, Data) VALUES(1, '%s')";
size_t st_len = strlen(st);

```

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

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

```

查询的时间必须足够长，以包含 SQL 字符串语句的大小和图像文件的大小。 使用`snprintf()`函数，将格式化的输出写入查询缓冲区。

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

```

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

## 从 MySQL 数据库中选择图像

在前面的示例中，我们已将图像插入数据库。 在下面的示例中，我们将从数据库中选择回插入的图像。

```perl
#include <my_global.h>
#include <mysql.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("woman2.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);
}

```

在此示例中，我们将从数据库中创建一个图像文件。

```perl
FILE *fp = fopen("woman2.jpg", "wb");

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

```

我们打开一个新的文件处理程序进行写入。

```perl
if (mysql_query(con, "SELECT Data FROM Images WHERE Id=1"))
{
    finish_with_error(con);
}

```

我们使用`Id` 1 从`Image`表中选择`Data`列。

```perl
MYSQL_ROW row = mysql_fetch_row(result);

```

该行包含原始数据。

```perl
unsigned long *lengths = mysql_fetch_lengths(result);

```

我们得到图像的长度。

```perl
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()`功能检查错误指示符。

```perl
int r = fclose(fp);

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

```

写入图像数据后，使用`fclose()`函数关闭文件处理程序。

这是 MySQL C API 教程。 您可能也对 [MySQL Python 教程](/db/mysqlpython/)， [MySQL Visual Basic 教程](/db/mysqlvb/)或 [MySQL PHP 教程](/databases/mysqlphptutorial/)， [PostgreSQL C 教程](/db/postgresqlc/)或[ [关于 ZetCode 的 SQLite C 教程](/db/sqlitec/)。