在 C/C++ 中访问 PostgreSQL
对于某些人来说,数据库可能非常令人生畏。我记得多年前为了避免学习如何从程序访问数据库而编写的一些复杂代码。但实际上访问数据库并不难,即使在 C/C++ 中也是如此。
似乎你可以编写的几乎任何应用程序都需要,或者可以受益于能够访问数据库。当然,大多数应用程序不需要像 PostgreSQL 这样的完整 RDBMS,但对于那些需要的应用程序来说,没有替代品。对于那些不需要完整 RDBMS 的应用程序,访问 SQLite 数据库可能是比管理一堆平面文件更好的选择。在本文中,我将演示访问 PostgreSQL 数据库有多么容易。在我的下一篇文章中,我将演示访问 SQLite 数据库并比较两者。
在我们能够就数据库进行任何有意义的对话之前,我们需要有一个简单的表定义,以及一些要放入其中的数据。
对于我们的目的来说,这应该足够好了
create table people (
id integer,
firstname varchar(20),
lastname varchar(20),
phonenumber char(10)
);
insert into people (id, firstname, lastname, phonenumber) values
(1, 'Fred', 'Flintstone', '5055551234');
insert into people (id, firstname, lastname, phonenumber) values
(2, 'Wilma', 'Flintstone', '5055551234');
insert into people (id, firstname, lastname, phonenumber) values
(3, 'Barny', 'Rubble', '5055554321');
这只是一个简单的表,用于保存我的一些史前朋友。奇迹般地,他们设法获得了电话服务。真是不可思议。
对于这个程序,我使用 C++ 库 libpq 来访问数据库。作为一名 C 程序员,我更喜欢原生 C 库而不是 C++ 库。但正如你将看到的,即使对于非 C++ 程序员来说,libpq 库也非常平易近人。
那么,让我们看一下一些源代码。
1 #include <stdio.h>
2 #include <postgresql/libpq-fe.h>
3 #include <string>
4
5 int main() {
6 PGconn *conn;
7 PGresult *res;
8 int rec_count;
9 int row;
10 int col;
11
12
13
14 conn = PQconnectdb("dbname=ljdata host=localhost user=dataman password=supersecret");
15
16 if (PQstatus(conn) == CONNECTION_BAD) {
17 puts("We were unable to connect to the database");
18 exit(0);
19 }
20
21 res = PQexec(conn,
22 "update people set phonenumber=\'5055559999\' where id=3");
23
24 res = PQexec(conn,
25 "select lastname,firstname,phonenumber from people order by id");
26
27 if (PQresultStatus(res) != PGRES_TUPLES_OK) {
28 puts("We did not get any data!");
29 exit(0);
30 }
31
32 rec_count = PQntuples(res);
33
34 printf("We received %d records.\n", rec_count);
35 puts("==========================");
36
37 for (row=0; row<rec_count; row++) {
38 for (col=0; col<3; col++) {
39 printf("%s\t", PQgetvalue(res, row, col));
40 }
41 puts("");
42 }
43
44 puts("==========================");
45
46 PQclear(res);
47
48 PQfinish(conn);
49
50 return 0;
51 }
只有 51 行,你可以看出这段代码不会很复杂。在顶部,第 2 行,我们看到了 PostgreSQL 库声明的必要包含。
在第 5-10 行,我们看到了 main() 的开始和一些局部变量声明。我们将使用 row 和 col 来循环遍历查询结果的行和列。rec_count 将保存我们的查询返回的记录数;我们将在循环中使用这个值。conn 变量指向一个连接句柄,该句柄保存库建立、维护和终止与数据库的连接所需的所有信息。最后,res 变量指向一个结果句柄,其中包含有关当前数据库查询的信息。
我们实际上在第 14 行连接到数据库。这是一个相当简单的代码片段。我们只需提供数据库的名称、它所在的宿主机以及我们的连接凭据。如果此函数返回 CONNECTION_BAD,我们知道我们没有连接成功,是时候退出了。我们在第 16-19 行检查了这个条件。
在第 21-22 行,我们执行一个简单的数据库更新。在这里,我们只需传入我们的连接句柄,以及一个包含我们的 SQL 更新语句的字符串/char[]。当然,我们的 SQL 语句可以包含任何有效的 SQL。PQexec() 函数返回一个结果句柄,对于这个特定的 SQL 语句,我们不需要它。
从第 24 行开始,事情变得更有趣了。这里我们从一个 SQL select 语句开始。我们将获取表中每个记录的 lastname、firstname 和 phonenumber 字段。第 27-30 行检查以确保我们实际上收到了来自查询的一些数据。在现实世界中,我们可能会在这里进行一些错误恢复。但对于我们的目的来说,打印错误消息并退出就足够了。
既然我们知道我们确实收到了数据,那么知道我们收到了多少数据会很好。我们在第 32 行获得记录计数。我们将在第 34 行使用这个计数来告诉用户期望多少数据。
第 37-42 行是我们实际处理查询结果的地方。在这种情况下,我们有一个嵌套循环,它循环遍历结果集的每一行。对于每一行,我们循环遍历每个字段。最后,我们打印每个字段,后跟一个制表符。在每一行的末尾,我们打印一个额外的回车符,以便每一行都显示在自己的行上。
关于第 39 行的 PQgetvalue() 函数,有几点值得一提。首先,请注意,我们传入了从我们开始查询时从 PQexec() 收到的结果句柄。另请注意,row 和 col 参数都是从 0 开始索引的。也就是说,第一个字段是通过将 col 设置为 0 而不是 1 来获取的。
在第 46 行和第 48 行,我们做一些内务处理。首先,我们告诉数据库我们已经完成了最后一个结果,并且不再需要结果句柄。然后,由于我们的程序即将完成,我们在第 48 行断开与数据库的连接。
我们使用类似如下的命令编译这个程序
g++ -lpq db.cpp -o db
一旦程序被编译,我们就有一个名为 db 的可执行文件。请注意,我们必须链接到 pq 库。
正如您现在可能已经猜到的那样,运行 db 程序会产生以下输出
We received 3 records.
==========================
Flintstone Fred 5055551234
Flintstone Wilma 5055551234
Rubble Barny 5055559999
==========================
所以,正如你所看到的,在 C/C++ 中访问 PostgreSQL 数据库并没有那么难。我建议将所有数据库功能封装在独立的函数中,并将它们全部保存在一个源文件中。然后,您程序的其余部分可以简单地调用您的函数来获取结果,而不必担心错误更正等问题。
下次,我将讨论一个类似的程序,该程序使用 SQLite 数据库。SQLite 数据库确实有一些限制,但不需要数据库服务器并且易于管理。但是,正如我之前所说,如果您的程序需要 RDBMS 的可扩展性和性能,PostgreSQL 是一个不错的选择,并且易于编程。