postgresql c 语言

99ANYc3cd6
预计阅读时长 25 分钟
位置: 首页 C语言 正文

核心概念:libpq

PostgreSQL 为 C 语言提供了一个官方的客户端库,叫做 libpq,它是与 PostgreSQL 服务器交互的标准接口,你所有的 C 程序,无论是简单的命令行工具还是复杂的应用程序,都将通过这个库来连接和操作数据库。

libpq 提供了一系列以 PQ 为前缀的函数,PQconnectdb, PQexec, PQgetvalue 等。


环境准备

在开始编码之前,你需要确保你的系统上安装了 PostgreSQL 开发包。

在 Linux (Ubuntu/Debian) 上:

# 安装 PostgreSQL 服务器和客户端开发库
sudo apt-get update
sudo apt-get install postgresql libpq-dev

postgresql 包包含服务器和客户端工具,libpq-dev 包含了编译 C 程序所需的头文件(.h)和链接库(.a / .so)。

在 macOS 上 (使用 Homebrew):

# 安装 PostgreSQL
brew install postgresql
# Homebrew 通常会自动配置好链接器,你不需要额外安装开发包

在 Windows 上:

在 Windows 上,最简单的方式是使用 PostgreSQL 官方安装程序,安装程序会提供一个名为 pgAdmin 的图形界面工具,并且会自动将 libpq 的头文件和库文件路径配置好,方便 Visual Studio 或 MinGW 等开发环境使用。


一个完整的示例程序

下面是一个从头到尾的完整示例,它会连接到 PostgreSQL,执行一个查询,并打印结果。

步骤 1: 创建数据库和表

你需要一个可以连接的数据库,使用 psql 命令行工具创建一个测试数据库和表。

# 切换到 postgres 用户(如果需要)
sudo -u postgres psql
-- 在 psql shell 中执行以下命令
CREATE DATABASE mytestdb;
\c mytestdb;
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(50) UNIQUE
);
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@example.com');
\q

步骤 2: 编写 C 代码

创建一个名为 main.c 的文件,并粘贴以下代码。

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h> // PostgreSQL 客户端头文件
// 错误处理函数,打印 PQerrorMessage 并退出
void exit_nicely(PGconn *conn) {
    PQfinish(conn);
    exit(1);
}
int main() {
    // 1. 定义连接字符串
    // 格式: "host=localhost dbname=mytestdb user=postgres password=yourpassword"
    // 注意:在生产环境中,不要在代码中硬编码密码!
    const char *conninfo = "host=localhost dbname=mytestdb user=postgres";
    // 2. 建立连接
    PGconn *conn = PQconnectdb(conninfo);
    // 检查连接状态
    if (PQstatus(conn) != CONNECTION_OK) {
        fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));
        exit_nicely(conn);
    }
    printf("Connection to database successful.\n");
    // 3. 执行 SQL 查询
    const char *query = "SELECT id, name, email FROM users;";
    PGresult *res = PQexec(conn, query);
    // 检查查询结果
    if (PQresultStatus(res) != PGRES_TUPLES_OK) {
        fprintf(stderr, "Query failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }
    printf("Query executed successfully.\n");
    // 4. 处理查询结果
    int nfields = PQnfields(res);       // 获取列数
    int ntuples = PQntuples(res);       // 获取行数
    // 打印列名
    for (int i = 0; i < nfields; i++) {
        printf("%-15s", PQfname(res, i));
    }
    printf("\n");
    // 打印每一行的数据
    for (int i = 0; i < ntuples; i++) {
        for (int j = 0; j < nfields; j++) {
            // PQgetvalue(res, row_num, col_num)
            printf("%-15s", PQgetvalue(res, i, j));
        }
        printf("\n");
    }
    // 5. 释放资源
    PQclear(res); // 释放 PGresult 对象
    PQfinish(conn); // 关闭连接并释放 PGconn 对象
    return 0;
}

步骤 3: 编译和链接

这是最关键的一步,你需要告诉编译器去哪里找 libpq 的头文件和库文件。

使用 gcc (Linux/macOS):

# -I 指定头文件路径,-L 指定库文件路径,-l 指定要链接的库
# 对于系统默认安装,-I 和 -L 通常可以省略
gcc -o pg_example main.c -I/usr/include/postgresql -L/usr/lib/postgresql -lpq

更简单的方式 (libpq-dev 已正确安装):

# gcc 通常能自动找到
gcc -o pg_example main.c -lpq

在 Windows (Visual Studio) 中:

  1. 在项目属性中,配置 "附加包含目录" 指向 PostgreSQL 的 include 目录(C:\Program Files\PostgreSQL\15\include)。
  2. 配置 "附加库目录" 指向 lib 目录(C:\Program Files\PostgreSQL\15\lib)。
  3. 在 "链接器" -> "输入" -> "附加依赖项" 中添加 libpq.lib

步骤 4: 运行程序

./pg_example

预期输出:

Connection to database successful.
Query executed successfully.
id             name           email          
1              Alice          alice@example.com
2              Bob            bob@example.com
3              Charlie        charlie@example.com

关键 libpq 函数详解

函数 描述
PQconnectdb(conninfo) 使用连接字符串建立连接,返回一个 PGconn* 指针。
PQstatus(conn) 检查连接状态,返回 CONNECTION_OKCONNECTION_BAD
PQerrorMessage(conn) 返回一个描述上次错误(连接或查询)的字符串。
PQfinish(conn) 关闭与数据库的连接并释放 PGconn 结构体及相关资源。必须调用!
PQexec(conn, query) 在连接上执行一个 SQL 命令,返回一个 PGresult* 指针。
PQresultStatus(res) 检查查询结果的状态,常用值:PGRES_COMMAND_OK (INSERT/UPDATE/DELETE 成功), PGRES_TUPLES_OK (SELECT 成功)。
PQclear(res) 释放 PGresult 结构体。每次使用 PQexec 后都必须调用!
PQntuples(res) 返回结果集中的行数。
PQnfields(res) 返回结果集中的列数。
PQfname(res, column_number) 获取指定列的名称。
PQgetvalue(res, row_number, column_number) 获取指定行和列的值(以 C 字符串形式返回)。

重要注意事项

1 安全性:不要硬编码密码

在上面的例子中,为了方便,我们在连接字符串中直接写了密码,这是一个严重的安全风险

正确做法:

  1. 使用 ~/.pgpass 文件(推荐)。

    • 在你的用户主目录下创建一个名为 .pgpass 的文件。
    • 格式为:hostname:port:database:username:password
    • localhost:5432:mytestdb:postgres:yourpassword
    • 设置文件权限为仅当前用户可读写:chmod 600 ~/.pgpass
    • 你的程序可以不写密码连接了:const char *conninfo = "host=localhost dbname=mytestdb user=postgres";
  2. 使用环境变量 PGPASSWORD

    • 在运行程序前设置环境变量:export PGPASSWORD=yourpassword
    • 程序连接时,libpq 会自动读取这个变量。

2 预处理语句(防止 SQL 注入)

对于动态的 SQL 查询(用户输入作为查询条件),直接拼接字符串是非常危险的,会导致 SQL 注入攻击。

libpq 提供了预处理语句来安全地处理这种情况。

示例:

// ... 连接代码同上 ...
const char *paramValues[1];
int paramLengths[1];
int paramFormats[1];
Oid paramTypes[1];
// 假设从用户那里获取了名字
const char *user_input_name = "Bob";
// 1. 准备语句
PGresult *res_prepare = PQprepare(conn, "get_user_by_name", "SELECT id, name, email FROM users WHERE name = $1;", 1, NULL);
if (PQresultStatus(res_prepare) != PGRES_COMMAND_OK) {
    fprintf(stderr, "Prepare statement failed: %s", PQerrorMessage(conn));
    PQclear(res_prepare);
    exit_nicely(conn);
}
PQclear(res_prepare);
// 2. 绑定参数
paramValues[0] = user_input_name;
paramLengths[0] = strlen(user_input_name);
paramFormats[0] = 0; // 文本格式
// 3. 执行语句
PGresult *res_execute = PQexecPrepared(conn, "get_user_by_name", 1, paramValues, paramLengths, paramFormats, 0);
if (PQresultStatus(res_execute) != PGRES_TUPLES_OK) {
    fprintf(stderr, "Execute prepared statement failed: %s", PQerrorMessage(conn));
    PQclear(res_execute);
    exit_nicely(conn);
}
// ... 处理 res_execute 结果 ...
PQclear(res_execute);
// ... 关闭连接 ...

在这个例子中,$1 是一个占位符。libpq 会确保 user_input_name 的值被正确地转义和处理,从而防止 SQL 注入。


使用 C 语言操作 PostgreSQL 的核心流程是:

  1. 包含头文件#include <libpq-fe.h>
  2. 连接数据库PQconnectdb() -> 检查 PQstatus()
  3. 执行命令PQexec() -> 检查 PQresultStatus()
  4. 处理结果:使用 PQntuples(), PQnfields(), PQgetvalue()
  5. 释放资源PQclear() 释放结果,PQfinish() 关闭连接
  6. 注意安全:使用 .pgpass 或环境变量管理密码,使用预处理语句处理用户输入。

libpq 功能非常强大,除了上述功能,还支持异步操作、复制、通知/监听等高级特性,你可以查阅 PostgreSQL 官方文档 获取更全面的信息。

-- 展开阅读全文 --
头像
dede 缩略图不识别jpeg
« 上一篇 01-03
c语言 touppercase
下一篇 » 01-03

相关文章

取消
微信二维码
支付宝二维码

目录[+]