Rust调用libpq访问PostgreSQL

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: Rust调用libpq访问PostgreSQL

Rust可以通过外来函数接口(FFI)访问C、C++编写的函数,因此我们可以通过PostgreSQL的libpq库访问PostgreSQL,本文旨在讲述方法及原理,因此例子代码没做任何封装,也没做错误处理。

libpq相关的常量、结构、函数声明均在libpq-fe.h中定义,我们需要先转换成rust的语法。这里先转换一些本例需要用到的。事实上我们是不需要手工转换的,Rust有提供bindgen工具帮助我们转换,只需要执行cargo install bindgen-cli安装,然后运行bindgen即可。

libpq相关声明 - lib_pq.rs

#![allow(non_camel_case_types)]#![allow(warnings)]pubtypeOid= ::std::os::raw::c_uint;
pubtypepg_int64= ::std::os::raw::c_long;
pubconstCONNECTION_OK: ConnStatusType=0;
pubconstCONNECTION_BAD: ConnStatusType=1;
pubconstCONNECTION_STARTED: ConnStatusType=2;
pubconstCONNECTION_MADE: ConnStatusType=3;
pubconstCONNECTION_AWAITING_RESPONSE: ConnStatusType=4;
pubconstCONNECTION_AUTH_OK: ConnStatusType=5;
pubconstCONNECTION_SETENV: ConnStatusType=6;
pubconstCONNECTION_SSL_STARTUP: ConnStatusType=7;
pubconstCONNECTION_NEEDED: ConnStatusType=8;
pubconstCONNECTION_CHECK_WRITABLE: ConnStatusType=9;
pubconstCONNECTION_CONSUME: ConnStatusType=10;
pubconstCONNECTION_GSS_STARTUP: ConnStatusType=11;
pubconstCONNECTION_CHECK_TARGET: ConnStatusType=12;
pubconstCONNECTION_CHECK_STANDBY: ConnStatusType=13;
pubtypeConnStatusType= ::std::os::raw::c_uint;
pubconstPGRES_POLLING_FAILED: PostgresPollingStatusType=0;
pubconstPGRES_POLLING_READING: PostgresPollingStatusType=1;
pubconstPGRES_POLLING_WRITING: PostgresPollingStatusType=2;
pubconstPGRES_POLLING_OK: PostgresPollingStatusType=3;
pubconstPGRES_POLLING_ACTIVE: PostgresPollingStatusType=4;
pubtypePostgresPollingStatusType= ::std::os::raw::c_uint;
pubconstPGRES_EMPTY_QUERY: ExecStatusType=0;
pubconstPGRES_COMMAND_OK: ExecStatusType=1;
pubconstPGRES_TUPLES_OK: ExecStatusType=2;
pubconstPGRES_COPY_OUT: ExecStatusType=3;
pubconstPGRES_COPY_IN: ExecStatusType=4;
pubconstPGRES_BAD_RESPONSE: ExecStatusType=5;
pubconstPGRES_NONFATAL_ERROR: ExecStatusType=6;
pubconstPGRES_FATAL_ERROR: ExecStatusType=7;
pubconstPGRES_COPY_BOTH: ExecStatusType=8;
pubconstPGRES_SINGLE_TUPLE: ExecStatusType=9;
pubconstPGRES_PIPELINE_SYNC: ExecStatusType=10;
pubconstPGRES_PIPELINE_ABORTED: ExecStatusType=11;
pubtypeExecStatusType= ::std::os::raw::c_uint;
pubconstPQTRANS_IDLE: PGTransactionStatusType=0;
pubconstPQTRANS_ACTIVE: PGTransactionStatusType=1;
pubconstPQTRANS_INTRANS: PGTransactionStatusType=2;
pubconstPQTRANS_INERROR: PGTransactionStatusType=3;
pubconstPQTRANS_UNKNOWN: PGTransactionStatusType=4;
pubtypePGTransactionStatusType= ::std::os::raw::c_uint;
pubconstPQERRORS_TERSE: PGVerbosity=0;
pubconstPQERRORS_DEFAULT: PGVerbosity=1;
pubconstPQERRORS_VERBOSE: PGVerbosity=2;
pubconstPQERRORS_SQLSTATE: PGVerbosity=3;
pubtypePGVerbosity= ::std::os::raw::c_uint;
#[repr(C)]#[derive(Debug, Copy, Clone)]pubstructpg_conn {
_unused: [u8; 0],
}
pubtypePGconn=pg_conn;
#[repr(C)]#[derive(Debug, Copy, Clone)]pubstructpg_result {
_unused: [u8; 0],
}
pubtypePGresult=pg_result;
#[repr(C)]#[derive(Debug, Copy, Clone)]pubstructpg_cancel {
_unused: [u8; 0],
}
pubtypePGcancel=pg_cancel;
//本例我们使用了以下函数//告诉Rust需要连接libpq#[link(name = "pq")]extern"C" {
pubfnPQconnectdb(conninfo: *const ::std::os::raw::c_char) ->*mutPGconn;
pubfnPQstatus(conn: *constPGconn) ->ConnStatusType;
pubfnPQresultStatus(res: *constPGresult) ->ExecStatusType;
pubfnPQexec(conn: *mutPGconn, query: *const ::std::os::raw::c_char) ->*mutPGresult;
pubfnPQnfields(res: *constPGresult) -> ::std::os::raw::c_int;
pubfnPQntuples(res: *constPGresult) -> ::std::os::raw::c_int;
pubfnPQclear(res: *mutPGresult);
pubfnPQfinish(conn: *mutPGconn);
pubfnPQerrorMessage(conn: *constPGconn) ->*mut ::std::os::raw::c_char;
pubfnPQfname(
res: *constPGresult,
field_num: ::std::os::raw::c_int,
    ) ->*mut ::std::os::raw::c_char;
pubfnPQgetvalue(
res: *constPGresult,
tup_num: ::std::os::raw::c_int,
field_num: ::std::os::raw::c_int,
    ) ->*mut ::std::os::raw::c_char;
}

main.rs -- 访问pg

usestd::ffi::{CString};
usestd::ffi::CStr;
usestd::process::{exit};
usecrate::lib_pq::{
CONNECTION_OK,
PGRES_TUPLES_OK,
PGconn,
PQclear,
PQconnectdb,
PQerrorMessage,
PQexec, PQfinish,
PQfname,
PQgetvalue,
PQnfields,
PQntuples,
PQresultStatus,
PQstatus};
//声明lib_pq模块modlib_pq;
fnexit_nicely(conn: *mutPGconn) {
unsafe {PQfinish(conn)};
exit(1);
}
fnmain() {
//数据库连接字符串lets=CString::new("dbname=postgres user=postgres password=xxxxxx host=localhost").unwrap();
//建立连接,调用外部函数需要用unsafe包装letconn=unsafe { PQconnectdb(s.as_ptr()) };
//检查状态ifunsafe{ PQstatus(conn) } !=CONNECTION_OK {
exit_nicely(conn);
    }
//执行sqlletsql=CString::new("select * from test1").unwrap();
letres=unsafe{PQexec(conn, sql.as_ptr())};
//检查执行结果ifunsafe{PQresultStatus(res)} !=PGRES_TUPLES_OK  {
println!("Exec sql failed: {:?}", unsafe{PQerrorMessage(conn)});
unsafe{PQclear(res)};
exit_nicely(conn);
    }
//获取字段名letn_fields=unsafe{PQnfields(res)};
foriin0..n_fields {
unsafe {
letn_field=  &*PQfname(res, i);
letv=CStr::from_ptr(n_field).to_string_lossy();
print!("{:<15}", v);
        }
    }
println!("{}", "");
//打印查询结果letn_tuples=unsafe{PQntuples(res)};
foriin0..n_tuples {
forjin0..n_fields {
unsafe {
letn_tuple= &*PQgetvalue(res, i, j);
letv=CStr::from_ptr(n_tuple).to_string_lossy();
print!("{:<15}", v);
            }
        }
println!("{}", "");
    }
//清理,释放资源unsafe {PQfinish(conn)};
}

整个例子非常简单,如果执行cargo build报错,提示找不到外部符号PQconnectdb之类,是因为rust找不到libpq的位置,这时需要生成一个构建文件build.rs,此文件必须与Cargo.toml位于同级目录,同时还需要在Cargo.toml的package区段里加上build = "build.rs"

构建文件 build.rs

fnmain() {
println!("cargo:rustc-link-search=/usr/local/pgsql15/lib");
println!("cargo:rustc-link-lib=pq");
}

构建文件就是告诉rust编译器去哪里找libpq。构建文件有很多选项,具体可参考Build Scripts

Cargo.toml

[package]
name = "pg_test"
version = "0.1.0"
edition = "2021"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
6月前
|
关系型数据库 开发工具 C语言
PostgreSQL libpq开发入门
简单入门C语言开发基于PostgreSQL libpq应用
104 0
|
9月前
|
前端开发 关系型数据库 测试技术
PostgreSQL 14通过libpq改进logging
PostgreSQL 14通过libpq改进logging
46 0
|
关系型数据库 Linux PostgreSQL
LINUX下载编译libpq(postgresql)
LINUX下载编译libpq(postgresql)
389 0
|
存储 关系型数据库 PostgreSQL
|
存储 关系型数据库 Shell
使用 Rust 开发 PostgreSQL 存储过程
pgxr 使用 Rust 来编写 PostgreSQL 的扩展函数(相当于存储过程)。 项目地址:https://github.com/clia/pgxr 使用这个星球上最快的、高效、安全、有趣的编程语言,来为世界上功能最强大的开源关系数据库编写库内的程序! 试想,当你从数据库中查询出 1000 条记录用于程序处理时,当你的程序是数据库内的程序时,你根本无需将这 1000 条结果通过 PostgreSQL 的通讯协议走网络传输到应用程序里,在应用程序里分配这么大一块内存来装这些数据,再来进行处理。
7207 0
|
关系型数据库 程序员 网络安全
PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库
第 33 章 libpq - C 库 目录 33.1. 数据库连接控制函数 33.1.1. 连接字符串 33.1.2. 参数关键词 33.2. 连接状态函数 33.3. 命令执行函数 33.3.
1421 0
|
关系型数据库 数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.21. 例子程序
33.21. 例子程序 这些例子和其他例子可以在源代码发布的src/test/examples目录中找到。 例 33.1. libpq 例子程序 1 /* * testlibpq.c * * 测试 libpq(PostgreSQL 前端库) 的 C 版本。
1136 0
|
关系型数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.20. 编译 libpq 程序
33.20. 编译 libpq 程序 要编译(即编译并且链接)一个使用libpq的程序,你需要做下列所有的事情: 包括libpq-fe.h头文件: #include <libpq-fe.h> 如果你无法这样做,那么你通常会从你的编译器得到像这样的错误消息: foo.
1389 0
|
安全 关系型数据库 PostgreSQL
PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.19. 在线程化程序中的行为
33.19. 在线程化程序中的行为 libpq默认是可再入的并且是线程安全的。你可能需要使用特殊的编译器命令行选项来编译你的应用代码。参考你的系统文档来了解如何编译启用线程的应用,或者在src/Makefile.global中查找PTHREAD_CFLAGS和PTHREAD_LIBS。
1124 0
|
安全 关系型数据库 网络安全
PostgreSQL 10.1 手册_部分 IV. 客户端接口_第 33 章 libpq - C 库_33.18. SSL 支持
33.18. SSL 支持 33.18.1. 服务器证书的客户端验证 33.18.2. 客户端证书 33.18.3. 不同模式中提供的保护 33.18.4. SSL 客户端文件使用 33.18.5. SSL 库初始化 PostgreSQL本地支持使用SSL 连接加密客户端/服务器通信以提高安全性。
1552 0