Rust调用libpq访问PostgreSQL

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: 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数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
7月前
|
关系型数据库 Linux PostgreSQL
这个错误是因为Flink CDC在尝试访问PostgreSQL的"decoderbufs"文件时,发现该文件不存在
【1月更文挑战第23天】【1月更文挑战第111篇】这个错误是因为Flink CDC在尝试访问PostgreSQL的"decoderbufs"文件时,发现该文件不存在
182 11
|
关系型数据库 开发工具 C语言
PostgreSQL libpq开发入门
简单入门C语言开发基于PostgreSQL libpq应用
|
关系型数据库 Linux PostgreSQL
Linux centos8 docker中安装postgresql12.4及远程访问设置
Linux centos8 docker中安装postgresql12.4及远程访问设置
682 0
|
6月前
|
关系型数据库 Unix 数据库
PostgreSQL开启远程访问
PostgreSQL开启远程访问
|
7月前
|
关系型数据库 Linux 数据安全/隐私保护
PostgreSQL【部署 02】在线安装PostgreSQL(Some psql features might not work 问题处理+角色密码设置+配置远程访问)
PostgreSQL【部署 02】在线安装PostgreSQL(Some psql features might not work 问题处理+角色密码设置+配置远程访问)
80 0
PostgreSQL【部署 02】在线安装PostgreSQL(Some psql features might not work 问题处理+角色密码设置+配置远程访问)
|
Java 关系型数据库 数据库
Spring Boot 学习研究笔记(七) -使用SpringData JPA访问PostgreSql数据库
Spring Boot 学习研究笔记(七) -使用SpringData JPA访问PostgreSql数据库
378 0
|
关系型数据库 网络安全 数据库
Windows中Postgresql之远程访问
Windows中Postgresql之远程访问
563 0
|
前端开发 关系型数据库 测试技术
PostgreSQL 14通过libpq改进logging
PostgreSQL 14通过libpq改进logging
93 0
|
SQL Oracle 网络协议
【.NET 6】使用EF Core 访问Oracle+Mysql+PostgreSQL并进行简单增改操作与性能比较
唠嗑一下。都在说去O或者开源,但是对于数据库选型来说,很多人却存在着误区。例如,去O,狭义上讲,是去Oracle数据库。但是从广义上来说,是去Oracle公司产品或者具有漂亮国垄断地位和需要商业授权的数据库产品。
514 0
【.NET 6】使用EF Core 访问Oracle+Mysql+PostgreSQL并进行简单增改操作与性能比较
|
监控 网络协议 关系型数据库
函数计算访问 PostgreSQL 数据库
函数计算访问 PostgreSQL 数据库自制脑图
118 0
函数计算访问 PostgreSQL 数据库