本篇教程使用C语言进行MySql数据库编程,内容覆盖了基本的使用C语言API进行MySql编程。本教程的程序在Ubuntu下进行编写和编译运行。为了能够使用C语言进行MySql数据库编程,我们需要安装MySQL的C开发库。
在基于Debian的Linux中开发库的安装命令如下。
sudo apt-get install libmysqlclient-dev
MySQL的安装命令如下,安装过程中我们需要给root账号设定密码。
sudo apt-get install mysql-server
第一个例子
在第一个例子中,我们测试一个MySQL函数的调用。
1
2
3
4
5
6
7
8
9
|
#include<my_global.h>
#include<mysql.h>
int
main(
int
argc,
char
**argv)
{
printf
(
"MySQL client version: %s\n"
,mysql_get_client_info());
return
0;
}
|
mysql_get_client_info()函数显示MySQL客户端版本。
1
2
|
#include<my_global.h>
#include<mysql.h>
|
这两行包含必要的头文件。mysql.h 是MySQL函数调用中最重要的头文件。my_global.h 包含一些全局函数的声明,除此之外,它还包含一些标准的输入/输出头文件。
1
|
gcc version.c -o version `mysql_config --cflags --libs`
|
在终端中输入该命令编译源代码文件。
除此之外,还有另一种编译方法。我的MySQL头文件安装目录为/usr/include/mysql/,库文件安装在/usr/lib/目录下,程序需要使用的库名称为libmysqlclient.so,因此另一种编译方法如下。当然,熟悉Linux编程的,我们可以将编译命令写到Makefile文件,这样不用每次都敲这些长长的编译命令了,直接一个make命令就可搞定了。
1
|
gcc version.c -o version -I/usr/include/include/ -lmysqlclient
|
1
2
|
ubuntu-root#./version
MySQL client version: 5.1.63
|
运行程序,在终端打印输出结果。
创建数据库
下面的程序将要创建一个数据库。程序的内容可以分为四个部分。
- 初始化数据库连接句柄结构
- 创建数据库连接
- 执行一个查询语句
- 关闭数据库连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include<my_global.h>
#include<mysql.h>
int
main(
int
argc,
char
**argv)
{
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"root"
,
"123456"
,NULL,0,NULL,0))
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
if
(mysql_query(con,
"CREATE DATABASE mydb"
))
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
fprintf
(stdout,
"%s\n"
,mysql_get_server_info(con));
fprintf
(stdout,
"%s\n"
,mysql_get_host_info(con));
mysql_close(con);
return
0;
}
|
这个例子演示了如何连接MySQL数据库系统并创建一个数据库mydb。
1
|
MYSQL *con = mysql_init(NULL);
|
mysql_init()函数分配或初始化一个MYSQL对象,该对象mysql_real_connect()函数连接数据库时需要使用。
1
2
3
4
5
|
if
(con == NULL)
{
fprintf
(stderr,
"%s\n"
, mysql_error(con));
return
-1;
}
|
通过检查mysql_init()函数的返回值确定初始化对象是否成功。如果失败,程序绘制终端打印错误信息并终止运行。
1
2
3
4
5
6
7
|
if
(mysql_real_connect(con,
"localhost"
,
"root"
,
"123456"
,
NULL, 0, NULL, 0) == NULL)
{
fprintf
(stderr,
"%s\n"
, mysql_error(con));
mysql_close(con);
exit
(1);
}
|
mysql_real_connect()函数建立一个指向数据库的连接。我们需要指定连接句柄,主机名,数据库账号和用户密码。函数最后四个参数是数据库名,端口号,unix套接字和客户端标记。我们需要超级用户权限来创建新的数据库。
1
2
3
4
5
6
|
if
(mysql_query(con,
"CREATE DATABASE mydb"
))
{
fprintf
(stderr,
"%s\n"
, mysql_error(con));
mysql_close(con);
exit
(1);
}
|
mysql_query()函数执行一个SQL语句。语句"CREATE DATABASE mydb"用于创建一个新的数据库mydb。
mysql_get_server_info()和mysql_get_host_info()函数分别获取服务器和主机信息。
1
|
mysql_close(con);
|
程序结束或出现异常前需要关闭数据库连接。最后,编译和运行程序,方法和第一个例子一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
ubuntu-root#./createdb
5.1.63-0ubuntu0.11.04.1
Localhost via UNIX socket
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
+--------------------+
3 rows in set (0.00 sec)
|
从上面显示结果来看,多了一个mydb数据库,这就是我们通过程序来创建的。
创建表并填充数据
在创建一张新的数据库表前,我们需要创建一个接下来需要使用的账号。
1
2
|
mysql> CREATE USER user123@localhost IDENTIFIED BY
'123456'
;
mysql> GRANT ALL ON mydb.* to user123@localhost;
|
上面我们创建了一个新的数据库账号user123,密码为123456。同时我们将mydb数据库的所有权限授予user123。
接下来的例子我们为mydb数据库创建一张新表并向表中插入数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
#include<my_global.h>
#include<mysql.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
finish_with_error(con);
}
if
(mysql_query(con,
"DROP TABLE IF EXISTS Phones"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"CREATE TABLE Phones(Id INT,Name TEXT,Price INT)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(1,'三星GALAXY S4',5199)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(2,'苹果iPhone5',5200)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(3,'联想K900',3299)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(4,'三星GALAXY Note II N7100(16GB)',4699)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(5,'华硕PadFone2',4999)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(6,'诺基亚920',2900)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(7,'三星GALAXY SIII',3470)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(8,'苹果iPhone 4S',4700)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(9,'HTC One',4480)"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Phones VALUES(10,'索尼L36H(Xperia Z)',3800)"
))
{
finish_with_error(con);
}
mysql_close(con);
return
0;
}
|
这个例子中我们使用mysql_query()函数创建一个表并向表中插入数据。
1
2
3
4
5
|
if
(mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
, 0, NULL, 0) == NULL)
{
finish_with_error(con);
}
|
连接数据库mydb。用户名和密码是user123和123456。第五个参数是要连接的数据库名。
1
2
3
|
if
(mysql_query(con,
"CREATE TABLE Phones(Id INT, Name TEXT, Price INT)"
)) {
finish_with_error(con);
}
|
创建表Phones。它由三个列:Id,Name和Price,类型分别为INT,TEXT和INT。
1
2
3
4
|
if
(mysql_query(con,
"INSERT INTO Phones VALUES(1,'三星GALAXY S4',5199)"
))
{
finish_with_error(con);
}
|
向Phones表中插入一行数据。程序中一共向表中插入了10行数据,这里我们选取了最近热销的前10款智能手机名称及其网上报价。
程序编译、运行后,我们可以查看数据库中的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
mysql> USE mydb;
Database changed
mysql> SHOW TABLES;
+----------------+
| Tables_in_mydb |
+----------------+
| Phones |
+----------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM Phones;
+------+----------------------------------+-------+
| Id | Name | Price |
+------+----------------------------------+-------+
| 1 | 三星GALAXY S4 | 5199 |
| 2 | 苹果iPhone5 | 5200 |
| 3 | 联想K900 | 3299 |
| 4 | 三星GALAXY Note II N7100(16GB) | 4699 |
| 5 | 华硕PadFone2 | 4999 |
| 6 | 诺基亚920 | 2900 |
| 7 | 三星GALAXY SIII | 3470 |
| 8 | 苹果iPhone 4S | 4700 |
| 9 | HTC One | 4480 |
| 10 | 索尼L36H(Xperia Z) | 3800 |
+------+----------------------------------+-------+
10 rows in set (0.00 sec)
|
我们查看了数据库mydb中所有的表并从Phones表中选择了所有数据进行显示。显示内容和程序中插入数据的内容是一样的。
从表中获取数据
接下来的例子,我们要从表中获取数据。
1
2
3
4
5
|
创建连接
执行查询语句
获取查询结果集
取出所有可用的行数据
释放结果集
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#include<my_global.h>
#include<mysql.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
finish_with_error(con);
}
if
(mysql_query(con,
"SELECT * FROM Phones"
))
{
finish_with_error(con);
}
MYSQL_RES *result = mysql_store_result(con);
if
(NULL == result)
{
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);
return
0;
}
|
本例打印表Phones中所有列的内容。
1
2
3
4
|
if
(mysql_query(con,
"SELECT * FROM Phones"
))
{
finish_with_error(con);
}
|
执行SQL语句,该语句从Phones表中获取所有数据。
1
|
MYSQL_RES *result = mysql_store_result(con);
|
使用mysql_store_result()函数获取结果集。MYSQL_RES是一个保存结果集的结构体。
1
|
int
num_fields = mysql_num_fields(result);
|
获取表中字段(列)的个数。
1
2
3
4
5
6
7
8
|
while
((row = mysql_fetch_row(result)))
{
for
(
int
i = 0; i < num_fields; i++)
{
printf
(
"%s "
, row[i] ? row[i] :
"NULL"
);
}
printf
(
"\n"
);
}
|
遍历结果集取出每行数据并在终端打印出来。
1
2
|
mysql_free_result(result);
mysql_close(con);
|
这里程序的编译需要加上-std=c99选项,否则代码中有些写法不支持,程序编译不过。
1
|
gcc -o retrieva_data retrieva_data.c -std=c99 `mysql_config --cflags --libs`
|
释放资源。
1
2
3
4
5
6
7
8
9
10
|
1 三星GALAXY S4 5199
2 苹果iPhone5 5200
3 联想K900 3299
4 三星GALAXY Note II N7100(16GB) 4699
5 华硕PadFone2 4999
6 诺基亚920 2900
7 三星GALAXY SIII 3470
8 苹果iPhone 4S 4700
9 HTC One 4480
10 索尼L36H(Xperia Z) 3800
|
最后插入行的id
有些时候,我们需要确定最后插入一行的id。通过调用mysql_insert_id()函数,我们可以确定最后插入一行的id。这个函数只有在我们在表中定义了一个具有AUTO_INCREMENT自增特性的列时才会生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
#include<my_global.h>
#include<mysql.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
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('易中天')"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Writers(Name) VALUES('于丹')"
))
{
finish_with_error(con);
}
if
(mysql_query(con,
"INSERT INTO Writers(Name) VALUES('纪连海')"
))
{
finish_with_error(con);
}
int
id = mysql_insert_id(con);
printf
(
"最后插入行的id是: %d\n"
,id);
mysql_close(con);
return
0;
}
|
本例创建了一个新表Writers。表中将会插入三行的数据,我们确定最后一行的id。
1
|
char
*sql =
"CREATE TABLE Writers(Id INT PRIMARY KEY AUTO_INCREMENT, Name TEXT)"
;
|
创建新表的SQL语句,其中Id列具有AUTO_INCREMENT类型。
1
|
int
id = mysql_insert_id(con);
|
mysql_insert_id()返回由先前的INSERT或UPDATE语句为具有AUTO_INCREMENT属性的列生成的一个值。
1
|
最后插入行的id是: 3
|
列标题
下面的例子,我们要从表中获取数据和对于的列标题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include<my_global.h>
#include<mysql.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
finish_with_error(con);
}
if
(mysql_query(con,
"SELECT * FROM Phones LIMIT 5"
))
{
finish_with_error(con);
}
MYSQL_RES *result = mysql_store_result(con);
if
(NULL == result)
{
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
(0 == i)
{
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);
return
0;
}
|
我们在终端打印表Phones前五行的数据,包括每列的标题名。
MYSQL_FIELD是一个包含字段信息的结构体,比如字段的名称,类型和大小等。字段对应的值不在该结构体中,它们包含在MYSQL_ROW结构体中。
1
2
3
4
5
6
7
8
9
|
if
(i == 0)
{
while
(field = mysql_fetch_field(result))
{
printf
(
"%s "
, field->name);
}
printf
(
"\n"
);
}
|
第一行包含列标题。mysql_fetch_field()函数返回MYSQL_FIELD结构体。我们从该结构体中获取列标题的名称。
1
2
3
4
5
6
|
Id Name Price
1 三星GALAXY S4 5199
2 苹果iPhone5 5200
3 联想K900 3299
4 三星GALAXY Note II N7100(16GB) 4699
5 华硕PadFone2 4999
|
多语句
我们可以在一次查询中执行多个SQL语句。我们需要在连接方法中设置CLIENT_MULTI_STATEMENTS标记。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#include<my_global.h>
#include<mysql.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
int
status = 0;
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,CLIENT_MULTI_STATEMENTS))
{
finish_with_error(con);
}
if
(mysql_query(con,"SELECT Name FROM Phones WHERE Id = 3;\
SELECT Name FROM Phones WHERE Id = 4;SELECT Name FROM Phones WHERE Id=7"))
{
finish_with_error(con);
}
do
{
MYSQL_RES *result = mysql_store_result(con);
if
(NULL == result)
{
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
(0 == status);
mysql_close(con);
return
0;
}
|
本例中我们在一个查询中执行了三个SELECT语句。
1
2
3
4
5
|
if
(mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
, 0, NULL, CLIENT_MULTI_STATEMENTS) == NULL)
{
finish_with_error(con);
}
|
mysql_real_connect()函数中最后一个参数指定客户端标记。它用于启用特定的特征。CLIENT_MULTI_STATEMENTS允许执行多个SQL语句。默认情况下不允许。
1
2
3
4
5
|
if
(mysql_query(con,"SELECT Name FROM Phones WHERE Id = 3;\
SELECT Name FROM Phones WHERE Id = 4;SELECT Name FROM Phones WHERE Id=7"))
{
finish_with_error(con);
}
|
查询包好三个SELECT语句。它们通过分号进行分割。\ 用于将一条语句进行分行。在多SQL语句中它没有任何执行效果。
1
2
3
|
do
{
...
}
while
(status == 0);
|
代码使用了do/while语句。获取的数据在循环中进行处理。我们单独获取每条SELECT语句的结果数据。
1
|
status = mysql_next_result(con);
|
我们需要获取多结果集。因此,我们需要调用mysql_next_result()函数。它它读取下一条语句的结果并返回一个状态值,通过该状态值我们可以确定是否还有查询结果存在。该函数返回0表示执行是正常的并且还有查询结果存在。当函数执行完毕后会返回-1并且已经没有查询结果存在了。最后,如果函数返回的值大于0,则表示函数调用出现错误。
1
2
3
4
|
if
(status > 0)
{
finish_with_error(con);
}
|
通过返回值检查是否有错误存在。
1
2
3
|
联想K900
三星GALAXY Note II N7100(16GB)
三星GALAXY SIII
|
向MySQL数据库中插入图像
图像本质是二进制数据。MySQL数据库中有一个特殊的数据类型可以用来存储二进制数据,该数据类型为BLOB(二进制大对象)。
1
|
mysql> CREATE TABLE Images(Id
INT
PRIMARY KEY, Data MEDIUMBLOB);
|
本例中我们需要创建一个新的表Images。图像的大小最大可以达到16MB。它取决于MEDIUMBLOB数据类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
#include<my_global.h>
#include<mysql.h>
#include<string.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
FILE
*fp =
fopen
(
"image.jpg"
,
"rb"
);
if
(NULL == fp)
{
fprintf
(stderr,
"无法打开图片文件。\n"
);
return
-1;
}
fseek
(fp,0,SEEK_END);
if
(
ferror
(fp))
{
fprintf
(stderr,
"fseek() 失败。\n"
);
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
}
int
flen =
ftell
(fp);
if
(-1 == flen)
{
perror
(
"出现错误"
);
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
return
-1;
}
fseek
(fp,0,SEEK_SET);
if
(
ferror
(fp))
{
fprintf
(stderr,
"fseek()失败。\n"
);
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
return
-1;
}
char
data[flen+1];
int
size =
fread
(data,1,flen,fp);
if
(
ferror
(fp))
{
fprintf
(stderr,
"fread()失败。\n"
);
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
return
-1;
}
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"mysql_init()失败。\n"
);
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
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);
return
0;
}
|
本例我们向Images表中插入一张图片。
1
|
FILE
*fp =
fopen
(
"image.jpg"
,
"rb"
);
|
打开图片文件。在当前工作路径下,我们需要有image.jpg文件。
1
|
fseek
(fp,0,SEEK_END);
|
通过fseek()函数将文件指针移到文件末尾。我们需要确定图片文件的大小。如果出现错误,错误指示器将会被设置。使用 fseek() 函数我们需要检测指示器。如果出现错误,我们也需要关闭打开的文件句柄。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int
flen =
ftell
(fp);
if
(flen == -1) {
perror
(
"error occured"
);
int
r =
fclose
(fp);
if
(r == EOF) {
fprintf
(stderr,
"cannot close file handler\n"
);
}
return
-1;
}
|
对于二进制流,ftell() 函数返回从文件开始处的字节数,比如图像文件的大小。如果出现错误,函数返回-1,错误号会被设置。perror()函数将错误号解释成对应的错误消息并将错误信息打印在表中错误输出流中。
1
|
char
data[flen+1];
|
该数组用于存储图像数据。
1
2
3
4
5
|
int
size =
fread
(data, 1, flen, fp);
int
r =
fclose
(fp);
if
(r == EOF) {
fprintf
(stderr,
"cannot close file handler\n"
);
}
|
从文件指针中读取数据并将数据存储在 data 数组中。函数返回成功读取的元素大小。读取完毕后我们需要使用 fclose() 函数关闭文件句柄。
1
2
|
char
chunk[2*size+1];
mysql_real_escape_string(con, chunk, data, size);
|
mysql_real_escape_string()函数将传给函数的字符串中存在潜在危险的字符前面加上转义字符\ 。这样做可以阻止SQL注入攻击。新的缓冲区大小必须至少为 2*size+1 。
1
2
|
char
*st =
"INSERT INTO Images(Id, Data) VALUES(1, '%s')"
;
size_t
st_len =
strlen
(st);
|
开始构建SQL语句。使用 strlen()函数我们可以确定SQL字符串的大小。
1
2
|
char
query[st_len + 2*size+1];
int
len = snprintf(query, st_len + 2*size+1, st, chunk);
|
查询必须有足够的空间用于存储SQL字符串语句大小和图片文件大小。使用snprintf()函数将格式化输出写入查询缓冲区。
1
2
3
4
|
if
(mysql_real_query(con, query, len))
{
finish_with_error(con);
}
|
使用mysql_real_query()函数执行查询。mysql_query()不能用于包含二进制数据的SQL语句中,我们需要使用 mysql_real_query() 函数替代。
从MySQL数据库中选择图像
在上个例子中,我们向数据库中插入一个图像文件。在下面的例子中,我们将从数据库中将插入的图像数据取出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
#include<my_global.h>
#include<mysql.h>
#include<string.h>
int
finish_with_error(MYSQL *con)
{
fprintf
(stderr,
"%s\n"
,mysql_error(con));
mysql_close(con);
return
-1;
}
int
main(
int
argc,
char
**argv)
{
FILE
*fp =
fopen
(
"image2.jpg"
,
"wb"
);
if
(NULL == fp)
{
fprintf
(stderr,
"无法打开图片文件。\n"
);
return
-1;
}
MYSQL *con = mysql_init(NULL);
if
(NULL == con)
{
fprintf
(stderr,
"mysql_init()失败。\n"
);
return
-1;
}
if
(NULL == mysql_real_connect(con,
"localhost"
,
"user123"
,
"123456"
,
"mydb"
,0,NULL,0))
{
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
(NULL == result)
{
finish_with_error(con);
}
MYSQL_ROW row = mysql_fetch_row(result);
unsigned
long
*lengths = mysql_fetch_lengths(result);
if
(NULL == lengths)
{
finish_with_error(con);
}
fwrite
(row[0],lengths[0],1,fp);
if
(
ferror
(fp))
{
fprintf
(stderr,
"fwrite() 失败。\n"
);
mysql_free_result(result);
mysql_close(con);
return
-1;
}
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
mysql_free_result(result);
mysql_close(con);
return
0;
}
|
本例我们从数据库中读取图像文件数据并保存到新创建的图像文件中。
1
2
3
4
5
6
7
|
FILE
*fp =
fopen
(
"image2.jpg"
,
"wb"
);
if
(fp == NULL)
{
fprintf
(stderr,
"cannot open image file\n"
);
return
-1;
}
|
打开新图像文件句柄用于写入从数据库中读取的图像文件数据。
1
2
3
4
|
if
(mysql_query(con,
"SELECT Data FROM Images WHERE Id=1"
))
{
finish_with_error(con);
}
|
从Images表中选择Id为1的Data列。
1
|
MYSQL_ROW row = mysql_fetch_row(result);
|
获取包含原始数据的行。
1
|
unsigned
long
*lengths = mysql_fetch_lengths(result);
|
获取图像数据的长度。
1
2
3
4
5
6
7
8
9
10
|
fwrite
(row[0], lengths[0], 1, fp);
if
(
ferror
(fp))
{
fprintf
(stderr,
"fwrite() failed\n"
);
mysql_free_result(result);
mysql_close(con);
return
-1;
}
|
使用fwrite()函数将获取的数据写入磁盘中。使用 ferror() 函数检查错误指示器。
1
2
3
4
5
|
int
r =
fclose
(fp);
if
(EOF == r)
{
fprintf
(stderr,
"无法关闭文件句柄。\n"
);
}
|
将图像数据写入文件后,我们需要使用fclose()关闭文件句柄。
程序编译运行后,在工作路径下会多一张图片文件image2.jpg,该文件和上例中的image.jpg是一样的。
这篇教程简单的介绍了如何使用C语言的API连接MySQL数据库,并进行一系列的相关操作,包括创建数据库,创建数据库表和插入、获取表中的数据。
参考文档:http://www.xftalk.com/archives/474