Coding 的痕迹

一位互联网奔跑者的网上日记

0%

(Rust) 记一次操作 PostgresSQL 的经历

本来标题是“惨痛的经历”,想想去掉了。 ——题记

(2021.2.16 更新:建议使用 sqlx 库,而不要直接使用 tokio-postgrespostgres

在项目中打算使用 PostgresSQL 做主力数据库,发现 Rust 下有个叫 postgres 的库,正好直接拿来用。 Rust 版本为 1.41,postgres 库版本 0.17。库官方给了 example,照着改了下,写了一小段业务代码做测试,然后一晚上就没了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use postgres::{Client, NoTls};
use chrono::{DateTime, Local};

struct UserBaseInfo
{
nick_name: String,
wechat_id: String,
student_no: String,
}

fn user_add(db_client: &mut Client, user_base: &UserBaseInfo)
{
let statement = db_client.prepare("INSERT INTO Users \
(nick_name, wechat_id, school_card, create_time) VALUES ($1, $2, $3, $4)")
.expect("预编译 SQL 错误");
let timestamp = chrono::Local::now();

db_client.execute(&statement,
&[&user_base.nick_name,
&user_base.wechat_id,
&user_base.student_no,
&timestamp
])
.expect("INSERT INTO 错误");
}

Cargo.toml 中加入:

1
2
3
[dependencies]
chrono = "0.4.10"
postgres = "0.17.1"

兴冲冲地去编译,得到了以下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
error[E0277]: the trait bound `chrono::datetime::DateTime<chrono::offset::local::Local>: postgres_typ
es::ToSql` is not satisfied
--> src\main.rs:22:27
|
22 | &timestamp
| ^^^^^^^^^^ the trait `postgres_types::ToSql` is not implemented for `c
hrono::datetime::DateTime<chrono::offset::local::Local>`
|
= note: required for the cast to the object type `dyn postgres_types::ToSql + std::marker::Sync`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

我的数据库中create_time字段是 timestamptz类型的。按照错误提示,DateTime<Local>没有实现ToSql trait。于是接着翻文档,开始没有太注意,后来在社区QQ群友的提示下,说我可能少加了feature。再一次看了一遍,发现文档中提到了:

In addition, some implementations are provided for types in third party crates. These are disabled by default; to opt into one of these implementations, activate the Cargo feature corresponding to the crate’s name prefixed by with-. For example, the with-serde_json-1 feature enables the implementation for the serde_json::Value type.

后面跟着一张表,有这样的数据:

Rust type Postgres type(s)
serde_json::Value JSON, JSONB
chrono::DateTime TIMESTAMP WITH TIME ZONE

但是实现(impl)chrono对应的with-是啥也没说。没说就自己编呗,试了几个,发现不行。在postgres的源码里搜feature也没有找到特别有用的信息,后来看到了这篇《【RUST】Restful API Server(8)–postgres里的时间格式》,开拓了思路,在postgres目录下的Cargo.toml中找到了支持的 features:

1
2
3
4
5
6
7
8
9
[features]
with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"]
with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"]
with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"]
with-geo-types-0_4 = ["tokio-postgres/with-geo-types-0_4"]
with-serde_json-1 = ["tokio-postgres/with-serde_json-1"]
with-uuid-0_8 = ["tokio-postgres/with-uuid-0_8"]
[badges.circle-ci]
repository = "sfackler/rust-postgres"

行,叫with-chrono-0_4。于是改了下项目的Cargo.toml

1
postgres = { version = "0.17.1", features = ["with-chrono-0_4"] }

再次编译运行,仍然找不到 ToSql trait。后来在 Stack Overflow 上找到了上个月的一个提问, 类似问题。我完全使用上面的代码,依然编译不过。

打算改用 diesel 库,但又有些不甘心。装好之后重启过一次电脑,发现cargo build又能跑通了。运行发现:

1
2
3
thread 'main' panicked at 'INSERT INTO 错误: Error { kind: ToSql(3), cause: Some(WrongType { postgres
: Type(Timestamp), rust: "chrono::datetime::DateTime<chrono::offset::local::Local>" }) }', src\libcor
e\result.rs:1188:5

又回去看了下简书上的那篇博文,发现作者用的是 0.16版本的 postgres 库(Stack overflow下的回答用的是 0.15版本,但接口不太样,遂放弃)。于是降版本,编译通过。猜测是升到0.17版本时库内部的代码或实现有些不稳定。

更加坚定了我用 diesel 的决心。以上。

欢迎关注我的其它发布渠道