Executor 与 Acquire
主线位置:查询主线的分派点。
query(...).bind(id).fetch_one(&pool)里,Query::fetch_one只是把自己交给executor.fetch_one(self);&Pool的Executor实现先acquire一条连接(连接池主线从这里开始),再调&mut PgConnection的Executor实现,后者从Execute里取出 SQL 和参数交给PgConnection::run。这一页讲两个 trait 和它们的实现者:Executor(拿什么执行)和Execute(执行什么),以及让一个函数同时收池和事务的Acquire。
flowchart TB q["Query::fetch_one"] --> pe["&Pool 的实现"] pe --> acq["pool.acquire()"] acq --> ce["&mut PgConnection"] ce --> fo["fetch_optional"] fo --> ex["take_arguments"] ex --> run["PgConnection::run"] tx["&mut *tx"] --> ce
十个方法,四个必须实现
Executor<'c> 的方法按值收 self,参数 query: E 是任何实现了 Execute 的东西。两个必须实现的查询方法长这样:
pub trait Executor<'c>: Send + Debug + Sized {
type Database: Database;
// ...
fn fetch_many<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxStream<
'e,
Result<
Either<<Self::Database as Database>::QueryResult, <Self::Database as Database>::Row>,
Error,
>,
>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>;
// ...
fn fetch_optional<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxFuture<'e, Result<Option<<Self::Database as Database>::Row>, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Self::Database>;
三个生命周期各管一件事:'c 是执行器本身借用的时长(&'c mut PgConnection),'q 是查询借用的时长,'e 是返回的 future 或 stream 能活多久;'c: 'e 和 'q: 'e 说的是「结果不能比连接和查询活得久」。
其余方法都是在这两个之上的默认实现:
| 方法 | 默认实现 | 返回 |
|---|---|---|
execute | execute_many(query).try_collect() | QueryResult,靠 Extend 把多段结果加起来 |
execute_many | fetch_many 只留 Left | 每条语句一个 QueryResult |
fetch | fetch_many 只留 Right | 行的 stream |
fetch_all | fetch(query).try_collect() | Vec<Row> |
fetch_one | fetch_optional,None 变成 Error::RowNotFound | 一行 |
prepare | prepare_with(query, &[]) | Statement |
fetch_many | 无,必须实现 | Either<QueryResult, Row> 的 stream |
fetch_optional | 无,必须实现 | Option<Row> |
prepare_with | 无,必须实现 | Statement |
describe | 无,必须实现,只在 offline feature 下存在 | Describe<DB> |
fetch_many 是最一般的形式:一条 SQL 里可能有多条语句,每条语句先产出若干行(Right),再产出一个 QueryResult(Left)。fetch_optional 单独列为必需,是因为驱动可以为「只要一行」做专门处理,而不是从 fetch_many 里取第一个就丢掉 stream(Postgres 的做法见本页的动脑筋)。
「只有 fetch_many 和 fetch_optional 是必需的」这个说法不对。只写这两个方法,rustc 会指出还缺什么(src/bin/exec_required.rs,平台 M2 Max、macOS、rustc 1.99.0,实验 crate 依赖 sqlx =0.9.0,开了 runtime-tokio、postgres、macros 等 feature):
use futures::future::BoxFuture;
use futures::stream::BoxStream;
use sqlx::Either;
use sqlx::postgres::{PgQueryResult, PgRow};
use sqlx::{Error, Execute, Executor, Postgres};
// An Executor that implements only the two methods the plan calls "required".
#[derive(Debug)]
struct Fake;
impl<'c> Executor<'c> for Fake {
type Database = Postgres;
fn fetch_many<'e, 'q: 'e, E>(self, _query: E) -> BoxStream<'e, Result<Either<PgQueryResult, PgRow>, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Postgres>,
{
Box::pin(futures::stream::empty())
}
fn fetch_optional<'e, 'q: 'e, E>(self, _query: E) -> BoxFuture<'e, Result<Option<PgRow>, Error>>
where
'c: 'e,
E: 'q + Execute<'q, Postgres>,
{
Box::pin(async { Ok(None) })
}
}
fn main() {}
error[E0046]: not all trait items implemented, missing: `prepare_with`, `describe`
--> src/bin/exec_required.rs:11:1
|
11 | impl<'c> Executor<'c> for Fake {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `prepare_with`, `describe` in implementation
|
= help: implement the missing item: `fn prepare_with<'e>(self, _: SqlStr, _: &'e [<<Self as Executor<'c>>::Database as sqlx::Database>::TypeInfo]) -> Pin<Box<(dyn futures::Future<Output = Result<<<Self as Executor<'c>>::Database as sqlx::Database>::Statement, sqlx::Error>> + std::marker::Send + 'e)>> { todo!() }`
= help: implement the missing item: `fn describe<'e>(self, _: SqlStr) -> Pin<Box<(dyn futures::Future<Output = Result<sqlx::Describe<<Self as Executor<'c>>::Database>, sqlx::Error>> + std::marker::Send + 'e)>> { todo!() }`
同一段代码放进一个只开 runtime-tokio 和 postgres、default-features = false 的 crate,只缺一个:
error[E0046]: not all trait items implemented, missing: `prepare_with`
--> src/main.rs:11:1
|
11 | impl<'c> Executor<'c> for Fake {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `prepare_with` in implementation
...
describe 带着 #[cfg(feature = "offline")],而 sqlx 的 macros feature 打开了 sqlx-core/offline。它是编译期宏拿列类型和可空性用的(见 describe 与可空性推断),所以只在用宏时存在。prepare 和 describe 的参数是 SqlStr 而不是 E: Execute:它们只需要 SQL 文本,没有参数要绑。
问: 0.9 已经能在 trait 里写 async fn 和 -> impl Future 了,Connection 的方法也改成了 impl Future,为什么 Executor 还返回 BoxFuture / BoxStream,每次查询多一次堆分配?
答: 试过,编译器不让。把这些 BoxFuture 去掉的 #3525(首次发布于 0.9.0)说明里写着:「I was not able to make these changes to the Executor and Acquire traits unfortunately because of a rustc limitation」,贴出的错误是 fetch_optional<'e, 'q: 'e, E> 这种带 'q: 'e 约束的签名报 lifetime bound not satisfied,并注明「this is a known limitation that will be removed in the future」(rust-lang/rust#100013)。
Execute:执行器眼里的一条查询
执行器不认识 Query,它只认 Execute:
pub trait Execute<'q, DB: Database>: Send + Sized {
/// Gets the SQL that will be executed.
fn sql(self) -> SqlStr;
/// Gets the previously cached statement, if available.
fn statement(&self) -> Option<&DB::Statement>;
/// Returns the arguments to be bound against the query string.
///
/// Returning `Ok(None)` for `Arguments` indicates to use a "simple" query protocol and to not
/// prepare the query. Returning `Ok(Some(Default::default()))` is an empty arguments object that
/// will be prepared (and cached) before execution.
///
/// Returns `Err` if encoding any of the arguments failed.
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments>, BoxDynError>;
/// Returns `true` if the statement should be cached.
fn persistent(&self) -> bool;
}
四个方法就是驱动需要的全部信息:SQL 文本、有没有现成的预备语句、参数(以及编码有没有出错)、要不要缓存。'q 在 0.9 里只剩一个用处:query_statement(&stmt) 构造的 Query 借用一个 &'q DB::Statement。
take_arguments 返回 Option 是这个 trait 最重要的约定:None 走简单查询协议(不预备、结果是文本格式、一条 SQL 里可以有多条语句),Some 哪怕是空的也走预备语句。实现者:
| 类型 | take_arguments | persistent |
|---|---|---|
任何 T: SqlSafeStr,比如 &'static str(blanket impl) | Ok(None) | true |
(T, Option<DB::Arguments>),T: SqlSafeStr | 元组第二项 | true |
Query / QueryAs / QueryScalar | Some(参数) 或编码错误 | .persistent(..) 设的值,默认 true |
RawSql | Ok(None) | false |
驱动怎么用这四个方法,看 &mut PgConnection 的 fetch_many:
fn fetch_many<'e, 'q, E>(
self,
mut query: E,
) -> BoxStream<'e, Result<Either<PgQueryResult, PgRow>, Error>>
where
'c: 'e,
E: Execute<'q, Self::Database>,
'q: 'e,
E: 'q,
{
// False positive: https://github.com/rust-lang/rust-clippy/issues/12560
#[allow(clippy::map_clone)]
let metadata = query.statement().map(|s| Arc::clone(&s.metadata));
let arguments = query.take_arguments().map_err(Error::Encode);
let persistent = query.persistent();
let sql = query.sql();
Box::pin(try_stream! {
let arguments = arguments?;
let mut s = pin!(self.run(sql, arguments, persistent, metadata).await?);
while let Some(v) = s.try_next().await? {
r#yield!(v);
}
Ok(())
})
}
take_arguments 在构造 stream 之前就调用了,但它的错误包成 Error::Encode 留到 stream 第一次被 poll 时才由 arguments? 抛出,self.run 根本不会执行。这就是「bind 编码失败推迟到执行时才报」在执行器一侧的样子,bind 一侧和实验见 Query、QueryAs 与 SqlSafeStr。sql(self) 按值拿走查询,所以排在最后。run 里发哪些协议消息见 扩展查询协议与语句缓存。
实验:哪些查询被预备了
同一条连接上,用五种方式各跑一条 select,每次之后看连接的语句缓存有几条(src/bin/execute_kinds.rs,连接的 search_path 设到自己的 schema):
use sqlx::{Connection, Executor, Row};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = exp_sqlx_core1::pool(1).await?;
let mut conn = pool.acquire().await?;
conn.clear_cached_statements().await?;
let cases: [(&str, &'static str); 5] = [
("&str", "select 1"),
("raw_sql", "select 2"),
("query, no bind", "select 3"),
("query, persistent(false)", "select 4"),
("query, one bind", "select $1::int4"),
];
for (i, (label, sql)) in cases.into_iter().enumerate() {
let row = match i {
0 => conn.fetch_one(sql).await?,
1 => sqlx::raw_sql(sql).fetch_one(&mut *conn).await?,
2 => sqlx::query(sql).fetch_one(&mut *conn).await?,
3 => sqlx::query(sql).persistent(false).fetch_one(&mut *conn).await?,
_ => sqlx::query(sql).bind(5_i32).fetch_one(&mut *conn).await?,
};
let v: i32 = row.try_get(0)?;
println!("{label:<25} value={v} cached_statements_size={}", conn.cached_statements_size());
}
let names: Vec<String> = sqlx::query_scalar("select name from pg_prepared_statements order by name")
.persistent(false)
.fetch_all(&mut *conn)
.await?;
println!("pg_prepared_statements: {names:?}");
Ok(())
}
&str value=1 cached_statements_size=0
raw_sql value=2 cached_statements_size=0
query, no bind value=3 cached_statements_size=1
query, persistent(false) value=4 cached_statements_size=1
query, one bind value=5 cached_statements_size=2
pg_prepared_statements: ["sqlx_s_1", "sqlx_s_2"]
&str和raw_sql的take_arguments是None,走简单查询,缓存不变。query("select 3")一个参数都没绑,照样被预备、进了缓存:分界线是「有没有Arguments对象」,不是「有没有参数」。query()构造时就放进了一个Some(Ok(Default::default()))。persistent(false)被预备了但没进缓存,服务端也没留下命名语句(0.8.6 起用未命名语句,见 扩展查询协议与语句缓存)。- 服务端的
pg_prepared_statements和客户端缓存对得上:两条命名语句sqlx_s_1、sqlx_s_2。
fetch_one 会不会读完所有行?fetch_one 的默认实现调 fetch_optional。下面这条 SQL 的前两行能算出来,第三行除以零(M2 Max、macOS、rustc 1.99.0、Postgres 16.14):
use futures::TryStreamExt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = exp_sqlx_core1::pool(1).await?;
// Rows 1 and 2 are fine; computing row 3 divides by zero.
let sql = "select 10 / (3 - g) from generate_series(1, $1) g";
let first = sqlx::query_scalar::<_, i32>(sql).bind(3).fetch(&pool).try_next().await;
println!("fetch().try_next(): {first:?}");
let one = sqlx::query_scalar::<_, i32>(sql).bind(3).fetch_one(&pool).await;
println!("fetch_one() : {one:?}");
Ok(())
}
两行各打印 Ok(Some(5)) / Ok(5),还是有一行是错误?
谁实现了 Executor
把 &mut tx 传给 fetch_one 时,rustc 会列出全部实现者(完整报错在下一节):
= help: the following other types implement trait `Executor<'c>`:
`&'c mut AnyConnection` implements `Executor<'c>`
`&'c mut PgConnection` implements `Executor<'c>`
`&'c mut PgListener` implements `Executor<'c>`
`&'c mut SqliteConnection` implements `Executor<'c>`
`&Pool<DB>` implements `Executor<'p>`
实现者只有两类:某个驱动的 &mut 连接,和 &Pool。PgListener 内部持有一条连接,它的实现转给那条连接。PgExecutor<'c> 只是 Executor<'c, Database = Postgres> 的别名 trait,带一个 blanket impl。
&Pool 的实现每次调用都重新取一条连接:
impl<'p, DB: Database> Executor<'p> for &'_ Pool<DB>
where
for<'c> &'c mut DB::Connection: Executor<'c, Database = DB>,
{
type Database = DB;
fn fetch_many<'e, 'q: 'e, E>(
self,
query: E,
) -> BoxStream<'e, Result<Either<DB::QueryResult, DB::Row>, Error>>
where
E: 'q + Execute<'q, Self::Database>,
{
let pool = self.clone();
Box::pin(try_stream! {
let mut conn = pool.acquire().await?;
let mut s = conn.fetch_many(query);
while let Some(v) = s.try_next().await? {
r#yield!(v);
}
Ok(())
})
}
// ...
self.clone() 克隆的是 Pool(一个 Arc),这样返回的 stream 不借用调用者的 &Pool。conn 是 PoolConnection,活在 stream 里,stream 读完或被 drop 时连接才归还。连续两次 fetch_one(&pool) 可能落在两条不同的物理连接上,trait 文档写的是「No guarantees are provided that successive queries run on the same physical database connection」。下面 Acquire 的实验里能看到这一点。acquire 内部怎么拿连接见 获取连接。
where 子句里那条 for<'c> &'c mut DB::Connection: Executor<'c, Database = DB> 不能省:Connection trait 不要求自己的可变引用是 Executor。同一个文件末尾留着一段被注释掉的 impl Executor for &mut PoolConnection<DB>,开头一行写着「Causes an overflow when evaluating &mut DB::Connection: Executor.」。
&mut *tx 从哪来
Transaction 和 PoolConnection 都不是 Executor(src/bin/tx_noderef.rs):
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = exp_sqlx_core1::pool(1).await?;
let mut tx = pool.begin().await?;
let one: i32 = sqlx::query_scalar("select 1").fetch_one(&mut tx).await?;
println!("{one}");
Ok(())
}
error[E0277]: the trait bound `&mut Transaction<'_, Postgres>: Executor<'_>` is not satisfied
--> src/bin/tx_noderef.rs:5:59
|
5 | let one: i32 = sqlx::query_scalar("select 1").fetch_one(&mut tx).await?;
| --------- ^^^^^^^ the trait `Executor<'_>` is not implemented for `&mut Transaction<'_, Postgres>`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Executor<'c>`:
`&'c mut AnyConnection` implements `Executor<'c>`
`&'c mut PgConnection` implements `Executor<'c>`
`&'c mut PgListener` implements `Executor<'c>`
`&'c mut SqliteConnection` implements `Executor<'c>`
`&Pool<DB>` implements `Executor<'p>`
note: required by a bound in `QueryScalar::<'q, DB, O, A>::fetch_one`
...
(同一个错误在这一行上报了三次,另外两次指向 query_scalar(...) 整个调用和 .await,这里只留第一次。)
修法是 &mut *tx:Transaction<'_, DB> 实现了 DerefMut<Target = DB::Connection>,*tx 是 PgConnection,&mut *tx 就是 &mut PgConnection。PoolConnection 同理。Executor 的文档注释里专门写了这件事。
&mut tx 在 0.6 是能用的0.7.0 之前,Transaction 和 PoolConnection 都有 Executor 实现,由每个驱动用宏生成。#2039(首次发布于 0.7.0)把各驱动拆成独立的 crate,CHANGELOG 写道:「The Executor impls for Transaction and PoolConnection have been deleted because they cannot exist in the new crate architecture without rewriting the Executor trait entirely.」原因有两层:「These cannot be blanket impls as it triggers an overflow in the compiler due to the lack of lazy normalization, and the driver crates cannot provide their own impls due to the orphan rule.」Transaction 定义在 sqlx-core,Executor 也在 sqlx-core,驱动 crate 两个都不拥有,孤儿规则不允许它写这个 impl;在 sqlx-core 里写 blanket impl 又会让编译器溢出,就是上面那段被注释掉的代码。
升级指引就是今天的写法:「&mut transaction -> &mut *transaction」。这段话后来由 #2713(首次发布于 0.7.3)抄进了 Executor 的文档注释,那个 PR 的起因是作者「got a little confused when I couldn't use &mut transaction for query.execute()」。
一个 E: Executor 只能用一次
Executor 的方法按值收 self。&Pool 是 Copy,可以反复用;&mut PgConnection 不是,fetch_one(&mut *conn) 每次都是一次新的重新借用。泛型参数 E 什么都不知道,用一次就被移走了(src/bin/executor_twice.rs):
use sqlx::PgExecutor;
async fn two_queries<'c, E>(e: E) -> sqlx::Result<i32>
where
E: PgExecutor<'c>,
{
let a: i32 = sqlx::query_scalar("select 1").fetch_one(e).await?;
let b: i32 = sqlx::query_scalar("select 2").fetch_one(e).await?;
Ok(a + b)
}
fn main() {
let _ = two_queries::<&sqlx::PgPool>;
}
error[E0382]: use of moved value: `e`
--> src/bin/executor_twice.rs:8:57
|
3 | async fn two_queries<'c, E>(e: E) -> sqlx::Result<i32>
| - move occurs because `e` has type `E`, which does not implement the `Copy` trait
...
7 | let a: i32 = sqlx::query_scalar("select 1").fetch_one(e).await?;
| - value moved here
8 | let b: i32 = sqlx::query_scalar("select 2").fetch_one(e).await?;
| ^ value used here after move
...
编译器建议加 Copy 约束,但那样就只剩 &Pool 能传进来。收 impl Executor 的函数适合只跑一条查询的场合;要跑多条,就收 Acquire。
Acquire:既收池又收事务
Acquire<'c> 只做一件事:交出一个能解引用成 DB::Connection 的东西,或者开一个事务:
pub trait Acquire<'c> {
type Database: Database;
type Connection: Deref<Target = <Self::Database as Database>::Connection> + DerefMut + Send;
fn acquire(self) -> BoxFuture<'c, Result<Self::Connection, Error>>;
fn begin(self) -> BoxFuture<'c, Result<Transaction<'c, Self::Database>, Error>>;
}
拿到的 Connection 可以反复 &mut *conn,于是一个函数里能跑任意多条查询。各实现者的 Connection 是什么:
| 实现者 | Connection | acquire | begin |
|---|---|---|---|
&Pool<DB> | PoolConnection<DB> | 真的从池里取,返回 BoxFuture<'static, _> | 取一条连接再 BEGIN,事务持有这条连接 |
&mut PgConnection(impl_acquire! 生成) | &mut PgConnection | ready(Ok(self)),不做任何事 | 在这条连接上 Transaction::begin |
&mut Transaction | &mut PgConnection | ready(Ok(&mut **self)) | 在事务的连接上再 Transaction::begin,此时深度大于 0,发 SAVEPOINT _sqlx_savepoint_N |
&mut PoolConnection | &mut PgConnection | ready(Ok(&mut **self)) | 在这条连接上 Transaction::begin |
&mut PgListener | &mut PgConnection | self.connection(),监听器内部那条连接 | 在那条连接上 begin |
Transaction::begin 交给驱动的 TransactionManager:连接的事务深度为 0 时发 BEGIN,否则发 SAVEPOINT _sqlx_savepoint_N。事务的嵌套和 Drop 时的回滚见 事务。
实验:同一个函数,四种实参
insert_and_count 用 Acquire 取连接,在同一条连接上查自己的 pg_backend_pid()、插一行、数行数(src/bin/acquire_generic.rs):
use sqlx::{Acquire, Postgres};
// One function, generic over anything that can hand out a Postgres connection.
async fn insert_and_count<'a, A>(conn: A, name: &str) -> anyhow::Result<(i32, i64)>
where
A: Acquire<'a, Database = Postgres>,
{
let mut conn = conn.acquire().await?;
let pid: i32 = sqlx::query_scalar("select pg_backend_pid()").fetch_one(&mut *conn).await?;
sqlx::query("insert into acq_demo(name) values ($1)").bind(name).execute(&mut *conn).await?;
let n: i64 = sqlx::query_scalar("select count(*) from acq_demo").fetch_one(&mut *conn).await?;
Ok((pid, n))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = exp_sqlx_core1::pool(2).await?;
sqlx::raw_sql("drop table if exists acq_demo; create table acq_demo(id serial primary key, name text not null)")
.execute(&pool).await?;
let (pid, n) = insert_and_count(&pool, "via &Pool").await?;
println!("&Pool : pid={pid} count={n}");
let mut tx = pool.begin().await?;
let (pid, n) = insert_and_count(&mut tx, "via &mut Transaction").await?;
println!("&mut Transaction : pid={pid} count={n}");
tx.rollback().await?;
let mut conn = pool.acquire().await?;
let (pid, n) = insert_and_count(&mut conn, "via &mut PoolConnection").await?;
println!("&mut PoolConn : pid={pid} count={n}");
let (pid, n) = insert_and_count(&mut *conn, "via &mut PgConnection").await?;
println!("&mut PgConnection: pid={pid} count={n}");
drop(conn);
let names: Vec<String> = sqlx::query_scalar("select name from acq_demo order by id").fetch_all(&pool).await?;
println!("after rollback : {names:?}");
sqlx::raw_sql("drop table acq_demo").execute(&pool).await?;
Ok(())
}
&Pool : pid=113 count=1
&mut Transaction : pid=114 count=2
&mut PoolConn : pid=113 count=2
&mut PgConnection: pid=113 count=3
after rollback : ["via &Pool", "via &mut PoolConnection", "via &mut PgConnection"]
- 四种实参都编译通过,函数体里三条查询在同一条连接上(每一行内部的 pid 是同一个,否则
count对不上)。 - 事务里
count=2看得到自己插的行;rollback之后这一行消失,后面的count又从 2 开始,最后的列表里没有via &mut Transaction。 &Pool那次和事务那次落在两条物理连接上(113 和 114):池里此时有两条连接,begin拿到的是另一条。用&Pool作Executor连续发两条查询时也会这样,所以「同一条连接上」的要求只能靠Acquire或显式acquire()满足。&mut conn(PoolConnection)和&mut *conn(PgConnection)都能传,前者走&mut PoolConnection的Acquire实现,后者走impl_acquire!生成的那个。
Acquire 的文档还提到一个坑:把这样的泛型函数放进需要 Send 的 future(比如 tokio::spawn)时,可能报「implementation of sqlx::Acquire is not general enough」,文档给的绕法是把 'a 拆成两个生命周期、手写 impl Future + Send + 'a 返回类型(#1015)。如果只想同时收连接和事务、不需要收池,文档建议更简单的签名:直接收 &mut PgConnection,调用方传 &mut *tx 或 &mut *conn。
动脑筋答案
fetch().try_next(): Ok(Some(5))
fetch_one() : Err(Database(PgDatabaseError { severity: Error, code: "22012", message: "division by zero", detail: None, hint: None, position: None, where: None, schema: None, table: None, column: None, data_type: None, constraint: None, file: Some("int.c"), line: Some(870), routine: Some("int4div") }))
fetch()是一个 stream,try_next只取第一行就停,第一行10 / (3 - 1) = 5没问题。fetch_one走fetch_optional,而&mut PgConnection的fetch_optional记下第一行之后不退出循环,把剩下的消息全部读完:
// With deferred constraints we need to check all responses as we
// could get a OK response (with uncommitted data), only to get an
// error response after (when the deferred constraint is actually
// checked).
let mut ret = None;
while let Some(result) = s.try_next().await? {
match result {
Either::Right(r) if ret.is_none() => ret = Some(r),
_ => {}
}
}
Ok(ret)
第三行除以零时服务端发来 ErrorResponse,try_next().await? 把它变成 Error::Database,已经拿到的第一行被丢掉。注释给的理由是延迟约束:一条 INSERT ... RETURNING 可能先返回一行,到语句结束检查约束时才报错,只看第一行会把失败当成功。代价是 fetch_one 会把整个结果集读完,Query::fetch_one 的文档因此建议保证查询最多返回一行(比如加 LIMIT 1)。
小结
Executor十个方法,必须实现fetch_many、fetch_optional、prepare_with,开了macros(offline)还有describe;其余都从fetch_many/fetch_optional派生。Execute交出 SQL、预备语句、参数和缓存开关;take_arguments返回None走简单查询,Some(哪怕为空)走预备语句。编码错误在执行时才抛出,run不会被调用。- 实现
Executor的只有各驱动的&mut 连接和&Pool;&Pool每次调用重新acquire,连续调用不保证同一条连接。Transaction和PoolConnection从 0.7 起要写&mut *tx,原因是孤儿规则加编译器溢出。 Executor方法按值收self,泛型E: Executor只能用一次;要跑多条查询的函数收Acquire,它对池、连接、事务、PoolConnection都有实现。- Postgres 的
fetch_optional(也就是fetch_one)会读完全部结果,后面的行出错整个调用就失败。