23. C语言操作PostgreSQL数据库:libpq库安装、PQconnectdb、PQexec、结果处理
PostgreSQL 在工业界用得挺广的,尤其是那些对数据一致性要求高的场景。我最早接触它是在一个金融交易系统里,那时候项目组选型,最后敲定了 PG。说实话,刚开始用 C 语言调 libpq 库的时候,我也踩过不少坑。今天咱们就把这块儿彻底捋清楚。
23.1 libpq 库的安装与配置
libpq 是 PostgreSQL 官方的 C 语言客户端库。说白了,它就是一座桥,让你的 C 程序能跟 PG 数据库对话。
在 Ubuntu/Debian 上安装:
sudo apt-get update
sudo apt-get install libpq-dev
在 CentOS/RHEL 上安装:
sudo yum install postgresql-devel
在 macOS 上(用 Homebrew):
brew install postgresql
安装完之后,头文件在 /usr/include/postgresql/ 目录下,库文件在 /usr/lib/x86_64-linux-gnu/ 或者类似位置。
pg_config --libdir 和 pg_config --includedir 来确认路径,这样最保险,不会找错地方。
编译链接命令:
gcc -o myapp myapp.c -I/usr/include/postgresql -lpq
这里 -lpq 就是链接 libpq 库。如果你用 CMake,可以这样写:
find_package(PostgreSQL REQUIRED)
include_directories(${PostgreSQL_INCLUDE_DIRS})
target_link_libraries(myapp ${PostgreSQL_LIBRARIES})
23.2 核心流程:连接、执行、处理结果
libpq 的操作流程其实很固定,就三步:建立连接 → 执行 SQL → 处理结果。我画了个图,你看一眼就明白了。
23.3 PQconnectdb:建立连接
PQconnectdb 是 libpq 里最基础的函数。你给它一个连接字符串,它帮你连上数据库。
#include <stdio.h>
#include <libpq-fe.h>
int main() {
const char *conninfo =
"host=localhost "
"port=5432 "
"dbname=mydb "
"user=myuser "
"password=mypass";
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, "连接失败: %s", PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
printf("连接成功!\n");
// ... 后续操作 ...
PQfinish(conn);
return 0;
}
这里有几个关键点:
- 连接字符串:可以包含 host、port、dbname、user、password 等参数。你也可以用环境变量
PGHOST、PGPORT等来设置。 - PQstatus:检查连接状态。返回
CONNECTION_OK才表示连接成功。 - PQerrorMessage:出错了用它看具体原因。
- PQfinish:用完一定要释放连接,不然会有内存泄漏。
23.4 PQexec:执行 SQL 语句
连接建立好了,接下来就是执行 SQL。PQexec 是最直接的方式,它把 SQL 字符串发给服务器,然后返回结果。
PGresult *res = PQexec(conn, "SELECT id, name FROM users LIMIT 5");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "查询失败: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
PQexec 返回一个 PGresult * 指针。你需要用 PQresultStatus 检查执行状态:
| 状态常量 | 含义 |
|---|---|
| PGRES_COMMAND_OK | INSERT、UPDATE、DELETE 等无返回行的命令执行成功 |
| PGRES_TUPLES_OK | SELECT 等有返回行的查询执行成功 |
| PGRES_FATAL_ERROR | 执行出错,用 PQerrorMessage 看详情 |
23.5 结果处理:获取数据
对于 SELECT 查询,拿到 PGresult 之后,你需要从中提取数据。libpq 提供了一组函数来做这件事。
int nrows = PQntuples(res); // 行数
int ncols = PQnfields(res); // 列数
for (int i = 0; i < nrows; i++) {
for (int j = 0; j < ncols; j++) {
const char *value = PQgetvalue(res, i, j);
const char *colname = PQfname(res, j);
printf("%s: %s\t", colname, value);
}
printf("\n");
}
常用结果处理函数:
PQntuples(res):获取结果行数PQnfields(res):获取结果列数PQfname(res, col_index):获取列名PQgetvalue(res, row_index, col_index):获取某个单元格的值(返回字符串)PQgetisnull(res, row_index, col_index):判断某个单元格是否为 NULLPQclear(res):释放结果集内存
const char *,所有值都以字符串形式返回。如果你需要整数或浮点数,记得用 atoi、atof 或 strtol 转换。另外,用完结果集一定要调用 PQclear,否则内存会一直涨。
23.6 完整示例:查询并打印用户表
下面是一个完整的例子,把上面讲的东西串起来:
#include <stdio.h>
#include <libpq-fe.h>
int main() {
// 1. 连接数据库
PGconn *conn = PQconnectdb("dbname=mydb user=myuser password=mypass");
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, "连接失败: %s\n", PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 2. 执行查询
PGresult *res = PQexec(conn, "SELECT id, name, email FROM users ORDER BY id");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "查询失败: %s\n", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
// 3. 处理结果
int rows = PQntuples(res);
int cols = PQnfields(res);
printf("共 %d 条记录:\n", rows);
for (int i = 0; i < rows; i++) {
int id = atoi(PQgetvalue(res, i, 0));
const char *name = PQgetvalue(res, i, 1);
const char *email = PQgetvalue(res, i, 2);
printf("ID: %d, Name: %s, Email: %s\n", id, name, email);
}
// 4. 清理
PQclear(res);
PQfinish(conn);
return 0;
}
23.7 避坑指南
最后,我把自己这些年踩过的坑总结一下,你写代码的时候多留个心眼:
- 连接字符串别写死:我建议从配置文件或环境变量读取,方便切换开发/测试/生产环境。
- 检查 PQstatus:连接成功后,不代表后续操作一定成功。网络波动、服务器重启都可能导致连接断开。
- PQclear 别忘:每个 PQexec 返回的 PGresult 都要释放。忘了的话,内存泄漏会让你头疼。
- 处理 NULL 值:用 PQgetisnull 判断,别直接拿 PQgetvalue 的结果去比较字符串 "NULL"。
- SQL 注入:别用 sprintf 拼接 SQL。用 PQexecParams 做参数化查询,安全又高效。
好了,libpq 的基础操作就这些。你想想看,其实核心就三个函数:PQconnectdb、PQexec、PQclear。把这三个玩转了,PostgreSQL 的 C 语言编程你就入门了。