Skip to main content

postgresql/
connection.rs

1//! Connection establishment, the background actor, and query execution.
2
3use std::{
4    collections::{HashMap, VecDeque},
5    fmt,
6    pin::Pin,
7    sync::{
8        Arc, Mutex,
9        atomic::{AtomicBool, Ordering},
10    },
11    task::{Context, Poll},
12};
13
14use bytes::{Bytes, BytesMut};
15use futures_util::Stream;
16use indexmap::IndexMap;
17use postgresql_protocol::{
18    DbError,
19    backend::BackendMessage,
20    error::Error as ProtocolError,
21    frontend,
22    oid::{Format, Oid},
23    scram::ScramClient,
24};
25use small_collections::SmallVec;
26use tokio::{
27    io::{AsyncReadExt, AsyncWriteExt},
28    net::TcpStream,
29    sync::{broadcast, mpsc, oneshot},
30};
31use xxhash::{Checksum, Xxh3_128};
32
33use crate::{
34    config::Config,
35    encode::{IsNull, ToSql},
36    error::Error,
37    row::{Columns, Row},
38    stream::PgStream,
39};
40
41const READ_BUFFER_SIZE: usize = 8192;
42
43/// Maximum number of distinct SQL strings interned per connection.
44const INTERNER_CAPACITY: usize = 256;
45
46/// Number of rows buffered per query before the actor applies backpressure.
47const ROW_CHANNEL_CAPACITY: usize = 32;
48
49/// Rows are coalesced into `CopyData` messages of at least this size.
50const COPY_FLUSH_THRESHOLD: usize = 8 * 1024;
51
52/// Number of `CopyData` payloads buffered before the actor applies
53/// backpressure. Bounded so that a slow `COPY TO STDOUT` consumer cannot make
54/// the client buffer an arbitrary amount of data.
55const COPY_CHANNEL_CAPACITY: usize = 16;
56
57/// How long a query is allowed to keep running after its stream was dropped,
58/// before a `CancelRequest` is sent to the server.
59///
60/// Dropping a stream first lets the connection drain the result normally (the
61/// actor keeps reading until `ReadyForQuery`); this timeout only fires for
62/// queries that would otherwise keep the connection busy for a long time. A
63/// random jitter of up to half this value is added so that dropping many
64/// streams at once does not produce a synchronized cancel storm.
65const ABANDONED_QUERY_CANCEL_DELAY: std::time::Duration = std::time::Duration::from_secs(2);
66
67/// Bound on how many cancellation requests may be pending at once.
68///
69/// A dropped stream is best-effort: when this many cancellations are already
70/// scheduled the request is skipped, and the actor still drains the query to
71/// completion.
72const CANCEL_QUEUE_CAPACITY: usize = 256;
73
74/// Maximum number of cancellation connections opened at once.
75const CANCEL_CONCURRENCY: usize = 16;
76
77/// Timeout for one cancellation attempt (connect + TLS + write).
78const CANCEL_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
79
80/// Largest number of backend messages accepted during the startup handshake.
81///
82/// A malicious or misbehaving server must not be able to keep the client in the
83/// authentication loop indefinitely with a stream of `ParameterStatus` /
84/// `NoticeResponse` messages (the handshake timeout also bounds this, but a
85/// message cap fails fast and cheaply).
86const MAX_AUTH_MESSAGES: usize = 1024;
87
88/// A server notification from `LISTEN` / `NOTIFY`.
89#[derive(Debug, Clone)]
90pub struct Notification {
91    /// Notifying backend process ID.
92    pub process_id: i32,
93    /// Channel name.
94    pub channel: String,
95    /// Notification payload.
96    pub payload: String,
97}
98
99/// A token that cancels the query currently running on a connection.
100///
101/// Cancelling opens a short-lived TLS connection to the server and sends a
102/// `CancelRequest`, exactly like `PQcancel`. It is a no-op if the query has
103/// already finished. If the server never sent `BackendKeyData` during startup
104/// (some non-PostgreSQL gateways), the token carries a zero key and the request
105/// has no effect.
106#[derive(Clone)]
107pub struct CancelToken {
108    config: Arc<Config>,
109    process_id: i32,
110    secret_key: i32,
111}
112
113impl CancelToken {
114    /// Requests cancellation of the connection's current query.
115    ///
116    /// # Errors
117    ///
118    /// Returns an error if the cancel connection cannot be established.
119    pub async fn cancel(&self) -> Result<(), Error> {
120        self.cancel_with_timeout(CANCEL_TIMEOUT).await
121    }
122
123    /// Like [`CancelToken::cancel`], but gives up after `timeout`.
124    pub(crate) async fn cancel_with_timeout(&self, timeout: std::time::Duration) -> Result<(), Error> {
125        tokio::time::timeout(timeout, async {
126            let address = format!("{}:{}", self.config.host, self.config.port);
127            let tcp = TcpStream::connect(&address).await.map_err(Error::Io)?;
128            let _ = tcp.set_nodelay(true);
129            let mut stream = negotiate_tls(tcp, &self.config).await?;
130            let mut buf = BytesMut::with_capacity(16);
131            frontend::cancel_request(&mut buf, self.process_id, self.secret_key);
132            stream.write_all(&buf).await.map_err(Error::Io)?;
133            stream.flush().await.map_err(Error::Io)?;
134            Ok(())
135        })
136        .await
137        .map_err(|_| Error::Config("timed out sending the cancellation request".into()))?
138    }
139}
140
141/// Number of cancellation jobs currently scheduled but not yet finished.
142///
143/// Shared process-wide so that dropping many streams cannot schedule more than
144/// [`CANCEL_QUEUE_CAPACITY`] cancellations at once.
145static PENDING_CANCELS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
146
147/// Process-wide cap on the number of cancellations running at the same time.
148///
149/// The semaphore is runtime-independent, so it keeps bounding concurrency even
150/// though each job runs on the runtime that scheduled it.
151fn cancel_semaphore() -> Arc<tokio::sync::Semaphore> {
152    static SEMAPHORE: std::sync::OnceLock<Arc<tokio::sync::Semaphore>> = std::sync::OnceLock::new();
153    SEMAPHORE
154        .get_or_init(|| Arc::new(tokio::sync::Semaphore::new(CANCEL_CONCURRENCY)))
155        .clone()
156}
157
158/// A reservation for one scheduled cancellation.
159///
160/// Released (and its pending-count decremented) when the job finishes or is
161/// dropped because its runtime shut down, so a dead runtime cannot leak slots.
162struct CancelSlot {
163    permit: Option<tokio::sync::OwnedSemaphorePermit>,
164}
165
166impl CancelSlot {
167    /// Reserves a slot, or returns `None` when the process is already at the
168    /// pending or concurrency limit (in which case the best-effort cancel is
169    /// skipped).
170    fn acquire() -> Option<Self> {
171        let previous = PENDING_CANCELS.fetch_add(1, Ordering::AcqRel);
172        if previous >= CANCEL_QUEUE_CAPACITY {
173            PENDING_CANCELS.fetch_sub(1, Ordering::AcqRel);
174            return None;
175        }
176        match cancel_semaphore().try_acquire_owned() {
177            Ok(permit) => Some(CancelSlot {
178                permit: Some(permit),
179            }),
180            Err(_) => {
181                PENDING_CANCELS.fetch_sub(1, Ordering::AcqRel);
182                None
183            }
184        }
185    }
186}
187
188impl Drop for CancelSlot {
189    fn drop(&mut self) {
190        drop(self.permit.take());
191        PENDING_CANCELS.fetch_sub(1, Ordering::AcqRel);
192    }
193}
194
195/// Cancels a query when dropped before it completes.
196struct QueryGuard {
197    completed: Arc<AtomicBool>,
198    cancel: Option<CancelToken>,
199}
200
201impl Drop for QueryGuard {
202    fn drop(&mut self) {
203        cancel_when_stuck(&self.completed, self.cancel.take());
204    }
205}
206
207/// Schedules a cancellation for a query that is still running after
208/// [`ABANDONED_QUERY_CANCEL_DELAY`].
209///
210/// The connection keeps draining the result in the background meanwhile, so
211/// most abandoned queries finish normally without a cancellation; the timeout
212/// only fires for queries that would otherwise occupy the connection for a
213/// long time. Cancelling is inherently racy: the server may already be done.
214///
215/// The job runs on the **current** runtime (never a process-wide worker bound
216/// to whichever runtime ran first), so a runtime shutting down cannot disable
217/// cancellation for the rest of the process. It is dropped — never queued
218/// unboundedly — when the process is already at its pending or concurrency
219/// limit.
220fn cancel_when_stuck(completed: &Arc<AtomicBool>, cancel: Option<CancelToken>) {
221    let Some(token) = cancel else { return };
222    if tokio::runtime::Handle::try_current().is_err() {
223        return;
224    }
225    let Some(slot) = CancelSlot::acquire() else { return };
226    let completed = completed.clone();
227    tokio::spawn(async move {
228        let _slot = slot;
229        let jitter_ms = (ABANDONED_QUERY_CANCEL_DELAY.as_millis() as u64) / 2;
230        let extra = rand::random_range(0..=jitter_ms);
231        tokio::time::sleep(ABANDONED_QUERY_CANCEL_DELAY + std::time::Duration::from_millis(extra)).await;
232        if !completed.load(Ordering::Acquire) {
233            let _ = token.cancel_with_timeout(CANCEL_TIMEOUT).await;
234        }
235    });
236}
237
238/// Interns SQL text so repeated queries send an `Arc<str>` clone instead of
239/// allocating a fresh `String` each time.
240struct Interner {
241    map: HashMap<u128, Arc<str>>,
242    order: VecDeque<u128>,
243    capacity: usize,
244}
245
246impl Interner {
247    fn new(capacity: usize) -> Self {
248        Interner {
249            map: HashMap::with_capacity(capacity),
250            order: VecDeque::with_capacity(capacity),
251            capacity,
252        }
253    }
254
255    fn intern(&mut self, sql: &str) -> Arc<str> {
256        let key = xxhash::xxh3_128(sql.as_bytes());
257        if let Some(existing) = self.map.get(&key) {
258            if existing.as_ref() == sql {
259                return existing.clone();
260            }
261            // A hash collision: drop the other string under this key (and its
262            // eviction slot) so the two entries cannot evict each other.
263            self.map.remove(&key);
264            self.order.retain(|&k| k != key);
265        }
266        if self.capacity == 0 {
267            return Arc::from(sql);
268        }
269        while self.map.len() >= self.capacity {
270            match self.order.pop_front() {
271                Some(old) => {
272                    self.map.remove(&old);
273                }
274                None => break,
275            }
276        }
277        let arc: Arc<str> = Arc::from(sql);
278        self.map.insert(key, arc.clone());
279        self.order.push_back(key);
280        arc
281    }
282}
283
284/// The 19-byte binary `COPY` file header: signature, flags and extension length.
285pub(crate) const COPY_HEADER: &[u8] = b"PGCOPY\n\xff\r\n\0\x00\x00\x00\x00\x00\x00\x00\x00";
286
287/// Parameters encoded for one query: the parameter OIDs plus their framed
288/// binary values (each prefixed with its 4-byte length, `-1` for `NULL`).
289pub(crate) struct EncodedParams {
290    pub oids: SmallVec<Oid, 8>,
291    pub values: BytesMut,
292}
293
294pub(crate) fn encode_params(params: &[&dyn ToSql]) -> Result<EncodedParams, Error> {
295    let mut values = BytesMut::with_capacity(params.len() * 16);
296    let mut oids: SmallVec<Oid, 8> = SmallVec::new();
297    for param in params {
298        oids.push(param.oid());
299        let len_pos = values.len();
300        values.extend_from_slice(&0i32.to_be_bytes());
301        let start = values.len();
302        match param.encode(&mut values)? {
303            IsNull::Null => {
304                values.truncate(len_pos);
305                values.extend_from_slice(&(-1i32).to_be_bytes());
306            }
307            IsNull::NotNull => {
308                let len = i32::try_from(values.len() - start)
309                    .map_err(|_| Error::Encode("parameter is larger than 2 GiB".into()))?;
310                values[len_pos..len_pos + 4].copy_from_slice(&len.to_be_bytes());
311            }
312        }
313    }
314    Ok(EncodedParams {
315        oids,
316        values,
317    })
318}
319
320pub(crate) struct QueryRequest {
321    sql: Arc<str>,
322    params: EncodedParams,
323    rows: mpsc::Sender<Result<Row, Error>>,
324    done: oneshot::Sender<Result<u64, Error>>,
325    max_rows: i32,
326    completed: Arc<AtomicBool>,
327}
328
329pub(crate) struct ScriptRequest {
330    sql: String,
331    done: oneshot::Sender<Result<u64, Error>>,
332}
333
334pub(crate) enum CopyInChunk {
335    Data(Bytes),
336    Done,
337    Fail(String),
338}
339
340pub(crate) struct CopyInRequest {
341    sql: String,
342    chunks: mpsc::Receiver<CopyInChunk>,
343    done: oneshot::Sender<Result<u64, Error>>,
344}
345
346pub(crate) struct CopyOutRequest {
347    sql: String,
348    chunks: mpsc::Sender<Result<Bytes, Error>>,
349    done: oneshot::Sender<Result<u64, Error>>,
350}
351
352pub(crate) enum Request {
353    Query(QueryRequest),
354    Script(ScriptRequest),
355    CopyIn(CopyInRequest),
356    CopyOut(CopyOutRequest),
357}
358
359/// A failed query attempt.
360///
361/// `rows_sent` records whether any `DataRow` was already handed to the caller's
362/// stream: a stale-plan error can only be retried safely when nothing has been
363/// delivered, otherwise the retry would duplicate those rows.
364struct AttemptError {
365    error: Error,
366    rows_sent: bool,
367}
368
369impl AttemptError {
370    /// A failure before any row was streamed.
371    fn before_rows(error: impl Into<Error>) -> Self {
372        AttemptError {
373            error: error.into(),
374            rows_sent: false,
375        }
376    }
377}
378
379impl From<Error> for AttemptError {
380    fn from(error: Error) -> Self {
381        AttemptError::before_rows(error)
382    }
383}
384
385impl From<ProtocolError> for AttemptError {
386    fn from(error: ProtocolError) -> Self {
387        AttemptError::before_rows(error)
388    }
389}
390
391/// A live connection to one PostgreSQL server.
392///
393/// `Connection` is a cheap, cloneable handle to a background actor that owns
394/// the socket. Cloning shares the same connection. All clones must be dropped
395/// for the connection to close.
396///
397/// Queries are queued and executed one at a time. The queue is unbounded, so a
398/// connection is best treated as "one in-flight query at a time": spawning
399/// many queries against a single connection without awaiting them queues their
400/// encoded parameters in memory. Use a [`Pool`](crate::Pool) for concurrency.
401#[derive(Clone)]
402pub struct Connection {
403    tx: mpsc::UnboundedSender<Request>,
404    interner: Arc<Mutex<Interner>>,
405    broken: Arc<AtomicBool>,
406    config: Arc<Config>,
407    notifications: broadcast::Sender<Notification>,
408    process_id: i32,
409    secret_key: i32,
410}
411
412impl fmt::Debug for Connection {
413    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414        f.debug_struct("Connection").finish_non_exhaustive()
415    }
416}
417
418struct CachedStatement {
419    sql: Arc<str>,
420    oids: SmallVec<Oid, 8>,
421    name: Arc<str>,
422    fields: Arc<Columns>,
423}
424
425impl CachedStatement {
426    fn matches(&self, sql: &str, oids: &[Oid]) -> bool {
427        self.sql.as_ref() == sql && self.oids.as_slice() == oids
428    }
429}
430
431struct Actor {
432    stream: PgStream,
433    read_buf: BytesMut,
434    write_buf: BytesMut,
435    cache: IndexMap<u128, CachedStatement>,
436    pending_closes: Vec<Arc<str>>,
437    statement_counter: u64,
438    broken: Arc<AtomicBool>,
439    zone: time::TimeZone,
440    notifications: broadcast::Sender<Notification>,
441    statement_cache_size: usize,
442    max_message_len: usize,
443}
444
445impl Actor {
446    fn mark_broken(&self) {
447        self.broken.store(true, Ordering::Release);
448    }
449
450    fn is_broken(&self) -> bool {
451        self.broken.load(Ordering::Acquire)
452    }
453}
454
455/// Resolves the configured IANA time zone (default UTC).
456fn resolve_zone(config: &Config) -> Result<time::TimeZone, Error> {
457    match &config.timezone {
458        Some(name) => time::TimeZone::named(name).map_err(|e| Error::Config(format!("invalid timezone `{name}`: {e}"))),
459        None => Ok(time::TimeZone::UTC),
460    }
461}
462
463/// Hashes a statement identity with XXH3-128, without allocating.
464///
465/// The hashed byte sequence is exactly `sql` followed by each parameter OID in
466/// big-endian order, so the hash is unchanged from when the input was built in
467/// a scratch buffer.
468fn statement_key(sql: &str, oids: &[Oid]) -> u128 {
469    let mut hasher = Xxh3_128::new();
470    hasher.update(sql.as_bytes());
471    for oid in oids {
472        hasher.update(&oid.to_be_bytes());
473    }
474    hasher.sum()
475}
476
477/// `0A000` (cached plan must not change result type) and `26000` (prepared
478/// statement does not exist) are recoverable by re-preparing.
479fn is_retryable_plan_error(code: &str) -> bool {
480    code == "0A000" || code == "26000"
481}
482
483/// Whether a failed attempt may be retried after re-preparing the statement.
484///
485/// A retry is only safe when the caller's stream has not yet received a row:
486/// re-running the query after rows were delivered would duplicate them. Only
487/// the first attempt is retried, and only for a stale-plan error.
488fn should_retry(attempt: u8, rows_sent: bool, error: &Error) -> bool {
489    attempt == 1 && !rows_sent && matches!(error, Error::Server(db) if is_retryable_plan_error(&db.code))
490}
491
492impl Connection {
493    /// Connects using a `postgres://` URL.
494    ///
495    /// # TLS
496    ///
497    /// TLS is always required; plaintext connections are not possible.
498    ///
499    /// | `sslmode` | Chain verified | Hostname verified |
500    /// | --- | --- | --- |
501    /// | `require` (default) | no | no |
502    /// | `verify-ca` | yes | no |
503    /// | `verify-full` | yes | yes |
504    ///
505    /// `disable`, `prefer` and `allow` are rejected. `verify-ca` and
506    /// `verify-full` read trusted roots from `sslrootcert` (a PEM file) or the
507    /// operating system trust store.
508    ///
509    /// ```no_run
510    /// # async fn run() -> Result<(), postgresql::Error> {
511    /// use postgresql::Connection;
512    ///
513    /// // Encrypted; the server certificate is not verified (default).
514    /// let conn = Connection::connect("postgres://user:pass@db.example.com/app").await?;
515    ///
516    /// // Verify the certificate chain and the hostname.
517    /// let verified = Connection::connect(
518    ///     "postgres://user:pass@db.example.com/app?sslmode=verify-full&sslrootcert=/etc/ssl/db-ca.pem",
519    /// )
520    /// .await?;
521    /// # let _ = (conn, verified);
522    /// # Ok(())
523    /// # }
524    /// ```
525    ///
526    /// # Errors
527    ///
528    /// Returns a configuration error for an invalid URL, or an I/O, TLS,
529    /// authentication or protocol error if the handshake fails.
530    pub async fn connect(url: &str) -> Result<Self, Error> {
531        let config = Config::parse(url)?;
532        Connection::connect_with_config(config).await
533    }
534
535    /// Connects using an already-parsed [`Config`].
536    pub(crate) async fn connect_with_config(config: Config) -> Result<Self, Error> {
537        // The timeout covers the whole handshake: TCP connect, TLS negotiation
538        // and authentication. A hostile (or merely slow) server that accepts
539        // TCP and then stalls must not hang `connect` forever.
540        let (stream, read_buf, process_id, secret_key) =
541            tokio::time::timeout(config.connect_timeout, establish(&config))
542                .await
543                .map_err(|_| Error::Config(format!("timed out connecting to {}:{}", config.host, config.port)))??;
544        let zone = resolve_zone(&config)?;
545        let broken = Arc::new(AtomicBool::new(false));
546        let (notifications, _) = broadcast::channel(64);
547        let actor = Actor {
548            stream,
549            read_buf,
550            write_buf: BytesMut::with_capacity(512),
551            cache: IndexMap::new(),
552            pending_closes: Vec::new(),
553            statement_counter: 0,
554            broken: broken.clone(),
555            zone,
556            notifications: notifications.clone(),
557            statement_cache_size: config.statement_cache_size,
558            max_message_len: config.max_message_len,
559        };
560        let (tx, rx) = mpsc::unbounded_channel();
561        tokio::spawn(actor.run(rx));
562        Ok(Connection {
563            tx,
564            interner: Arc::new(Mutex::new(Interner::new(INTERNER_CAPACITY))),
565            broken,
566            config: Arc::new(config),
567            notifications,
568            process_id,
569            secret_key,
570        })
571    }
572
573    /// Returns `true` if the connection has been closed or hit an I/O error.
574    pub fn is_closed(&self) -> bool {
575        self.broken.load(Ordering::Acquire) || self.tx.is_closed()
576    }
577
578    /// The configured maximum backend message / row size, in bytes.
579    pub(crate) fn max_message_len(&self) -> usize {
580        self.config.max_message_len
581    }
582
583    /// Subscribes to `LISTEN` / `NOTIFY` notifications.
584    ///
585    /// The returned receiver yields notifications delivered on this connection
586    /// (after `LISTEN`). Multiple subscribers are supported.
587    pub fn notifications(&self) -> broadcast::Receiver<Notification> {
588        self.notifications.subscribe()
589    }
590
591    /// Returns a token that can cancel the currently running query.
592    pub fn cancel_token(&self) -> CancelToken {
593        CancelToken {
594            config: self.config.clone(),
595            process_id: self.process_id,
596            secret_key: self.secret_key,
597        }
598    }
599
600    /// Verifies the connection is usable with a trivial round trip.
601    pub async fn ping(&self) -> Result<(), Error> {
602        self.execute("SELECT 1", &[]).await?;
603        Ok(())
604    }
605
606    fn intern(&self, sql: &str) -> Arc<str> {
607        self.interner
608            .lock()
609            .unwrap_or_else(|poisoned| poisoned.into_inner())
610            .intern(sql)
611    }
612
613    pub(crate) async fn fetch_rows(&self, sql: &str, params: &[&dyn ToSql], max_rows: i32) -> Result<RowStream, Error> {
614        let sql = self.intern(sql);
615        let encoded = encode_params(params)?;
616        let (rows, rx) = mpsc::channel(ROW_CHANNEL_CAPACITY);
617        let (done, _done_rx) = oneshot::channel();
618        let completed = Arc::new(AtomicBool::new(false));
619        self.tx
620            .send(Request::Query(QueryRequest {
621                sql,
622                params: encoded,
623                rows,
624                done,
625                max_rows,
626                completed: completed.clone(),
627            }))
628            .map_err(|_| Error::Closed)?;
629        Ok(RowStream::new(
630            rx,
631            completed,
632            // A limited query (`fetch_one`/`fetch_optional`) stops itself, so it
633            // never needs a cancel request; only an abandoned unbounded stream
634            // does.
635            if max_rows == 0 { Some(self.cancel_token()) } else { None },
636        ))
637    }
638
639    pub(crate) async fn execute(&self, sql: &str, params: &[&dyn ToSql]) -> Result<u64, Error> {
640        let sql = self.intern(sql);
641        let encoded = encode_params(params)?;
642        // `execute` does not return rows: drop the receiver up front so the
643        // actor can skip them instead of blocking on an unread channel. Rows
644        // produced by the statement are discarded; the affected-row count is
645        // taken from the command tag.
646        let (rows, rx) = mpsc::channel(1);
647        drop(rx);
648        let (done, done_rx) = oneshot::channel();
649        let completed = Arc::new(AtomicBool::new(false));
650        let guard = QueryGuard {
651            completed: completed.clone(),
652            cancel: Some(self.cancel_token()),
653        };
654        self.tx
655            .send(Request::Query(QueryRequest {
656                sql,
657                params: encoded,
658                rows,
659                done,
660                max_rows: 0,
661                completed: completed.clone(),
662            }))
663            .map_err(|_| Error::Closed)?;
664        let result = match done_rx.await {
665            Ok(result) => result,
666            Err(_) => Err(Error::Closed),
667        };
668        completed.store(true, Ordering::Release);
669        drop(guard);
670        result
671    }
672
673    /// Runs a multi-statement script via the simple query protocol.
674    ///
675    /// # Errors
676    ///
677    /// Returns an error if the connection is closed or the server rejects a
678    /// statement.
679    pub(crate) async fn execute_script(&self, sql: &str) -> Result<u64, Error> {
680        let (done, done_rx) = oneshot::channel();
681        self.tx
682            .send(Request::Script(ScriptRequest {
683                sql: sql.to_string(),
684                done,
685            }))
686            .map_err(|_| Error::Closed)?;
687        match done_rx.await {
688            Ok(result) => result,
689            Err(_) => Err(Error::Closed),
690        }
691    }
692
693    /// Enqueues a statement without waiting for its result.
694    ///
695    /// Used by [`Transaction`](crate::Transaction)'s `Drop` to issue a
696    /// `ROLLBACK` before the connection is returned to its pool.
697    pub(crate) fn enqueue(&self, sql: &str) {
698        let sql = self.intern(sql);
699        let Ok(params) = encode_params(&[]) else {
700            return;
701        };
702        let (rows, rx) = mpsc::channel(1);
703        drop(rx);
704        let (done, _done_rx) = oneshot::channel();
705        let completed = Arc::new(AtomicBool::new(false));
706        let _ = self.tx.send(Request::Query(QueryRequest {
707            sql,
708            params,
709            rows,
710            done,
711            max_rows: 0,
712            completed,
713        }));
714    }
715
716    /// Starts a binary `COPY ... FROM STDIN` and returns a writer.
717    ///
718    /// Values are encoded into a shared buffer and coalesced into `CopyData`
719    /// messages of at least 8 KiB. Dropping the writer without finishing
720    /// aborts the COPY and leaves the connection usable.
721    ///
722    /// # Errors
723    ///
724    /// Returns an error if the connection is closed.
725    pub(crate) async fn copy_in(&self, sql: &str) -> Result<CopyInWriter, Error> {
726        let (tx, rx) = mpsc::channel(16);
727        let (done, done_rx) = oneshot::channel();
728        self.tx
729            .send(Request::CopyIn(CopyInRequest {
730                sql: sql.to_string(),
731                chunks: rx,
732                done,
733            }))
734            .map_err(|_| Error::Closed)?;
735        // Binary COPY begins with the 19-byte file header.
736        let _ = tx.send(CopyInChunk::Data(Bytes::from_static(COPY_HEADER))).await;
737        Ok(CopyInWriter {
738            tx,
739            done: Some(done_rx),
740            finished: false,
741            buf: Mutex::new(BytesMut::with_capacity(COPY_FLUSH_THRESHOLD * 2)),
742        })
743    }
744
745    /// Starts a binary `COPY ... TO STDOUT` and returns a stream of raw
746    /// `CopyData` payloads.
747    ///
748    /// # Errors
749    ///
750    /// Returns an error if the connection is closed.
751    pub(crate) async fn copy_out(&self, sql: &str) -> Result<CopyOutStream, Error> {
752        let (chunks, rx) = mpsc::channel(COPY_CHANNEL_CAPACITY);
753        let (done, _done_rx) = oneshot::channel();
754        self.tx
755            .send(Request::CopyOut(CopyOutRequest {
756                sql: sql.to_string(),
757                chunks,
758                done,
759            }))
760            .map_err(|_| Error::Closed)?;
761        Ok(CopyOutStream {
762            rx,
763        })
764    }
765}
766
767/// Encodes one binary-format `COPY` row: an `Int16` field count followed by
768/// each field as an `Int32` length (or `-1` for `NULL`) and its bytes.
769pub fn encode_copy_row(values: &[&dyn ToSql], buf: &mut BytesMut) -> Result<(), Error> {
770    let count = u16::try_from(values.len())
771        .map_err(|_| Error::Encode(format!("too many columns in COPY row: {} (max {})", values.len(), u16::MAX)))?;
772    buf.extend_from_slice(&count.to_be_bytes());
773    for value in values {
774        let len_pos = buf.len();
775        buf.extend_from_slice(&0i32.to_be_bytes());
776        let start = buf.len();
777        match value.encode(buf)? {
778            IsNull::Null => {
779                buf.truncate(len_pos);
780                buf.extend_from_slice(&(-1i32).to_be_bytes());
781            }
782            IsNull::NotNull => {
783                let len = i32::try_from(buf.len() - start)
784                    .map_err(|_| Error::Encode("COPY field is larger than 2 GiB".into()))?;
785                buf[len_pos..len_pos + 4].copy_from_slice(&len.to_be_bytes());
786            }
787        }
788    }
789    Ok(())
790}
791
792/// A writer for a binary `COPY ... FROM STDIN`.
793pub(crate) struct CopyInWriter {
794    tx: mpsc::Sender<CopyInChunk>,
795    done: Option<oneshot::Receiver<Result<u64, Error>>>,
796    finished: bool,
797    buf: Mutex<BytesMut>,
798}
799
800impl CopyInWriter {
801    /// Encodes one value into the pending `CopyData` buffer, flushing it once
802    /// it is large enough (at least 8 KiB). Small values are coalesced into
803    /// larger `CopyData` messages, so a bulk load of short rows does not cost
804    /// one syscall and one TLS record per row.
805    ///
806    /// # Errors
807    ///
808    /// Returns an error if the connection is closed.
809    /// Used by [`crate::copy::CopyIn`] to avoid one allocation per row.
810    pub(crate) async fn append(&self, encode: impl FnOnce(&mut BytesMut) -> Result<(), Error>) -> Result<(), Error> {
811        let chunk = {
812            let mut buf = self.buf.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
813            encode(&mut buf)?;
814            if buf.len() < COPY_FLUSH_THRESHOLD {
815                None
816            } else {
817                Some(buf.split().freeze())
818            }
819        };
820        match chunk {
821            Some(chunk) => self.tx.send(CopyInChunk::Data(chunk)).await.map_err(|_| Error::Closed),
822            None => Ok(()),
823        }
824    }
825
826    /// Sends the COPY trailer and completes the COPY.
827    ///
828    /// Returns the number of rows copied.
829    ///
830    /// # Errors
831    ///
832    /// Returns an error if the server rejects the COPY.
833    pub async fn finish(mut self) -> Result<u64, Error> {
834        // Binary COPY ends with a 16-bit -1 word.
835        self.append(|buf| {
836            buf.extend_from_slice(&[0xff, 0xff]);
837            Ok(())
838        })
839        .await?;
840        if let Some(rest) = {
841            let mut buf = self.buf.lock().unwrap_or_else(|p| p.into_inner());
842            (!buf.is_empty()).then(|| buf.split().freeze())
843        } {
844            let _ = self.tx.send(CopyInChunk::Data(rest)).await;
845        }
846        let _ = self.tx.send(CopyInChunk::Done).await;
847        let done = self.done.take().expect("COPY already finished");
848        self.finished = true;
849        match done.await {
850            Ok(result) => result,
851            Err(_) => Err(Error::Closed),
852        }
853    }
854}
855
856impl Drop for CopyInWriter {
857    fn drop(&mut self) {
858        if !self.finished {
859            // Best effort: when the channel is full this is lost, and the actor
860            // aborts the COPY on its own when the channel closes (see
861            // `Actor::handle_copy_in`), so the connection can never be left in
862            // COPY-in mode.
863            let _ = self.tx.try_send(CopyInChunk::Fail("COPY cancelled".into()));
864        }
865    }
866}
867
868/// A stream of raw `CopyData` payloads from a binary `COPY ... TO STDOUT`.
869pub(crate) struct CopyOutStream {
870    pub(crate) rx: mpsc::Receiver<Result<Bytes, Error>>,
871}
872
873impl Stream for CopyOutStream {
874    type Item = Result<Bytes, Error>;
875
876    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
877        self.rx.poll_recv(cx)
878    }
879}
880
881/// A stream of rows from a query.
882///
883/// A pooled connection is held for as long as its stream is alive, so a query
884/// started on a [`crate::Pool`] cannot be interleaved with another checkout of
885/// the same connection.
886pub struct RowStream {
887    rx: mpsc::Receiver<Result<Row, Error>>,
888    completed: Arc<AtomicBool>,
889    cancel: Option<CancelToken>,
890    keepalive: Option<Box<dyn Send + 'static>>,
891}
892
893impl RowStream {
894    pub(crate) fn new(
895        rx: mpsc::Receiver<Result<Row, Error>>,
896        completed: Arc<AtomicBool>,
897        cancel: Option<CancelToken>,
898    ) -> Self {
899        RowStream {
900            rx,
901            completed,
902            cancel,
903            keepalive: None,
904        }
905    }
906
907    /// Ties the lifetime of `guard` (typically a pooled connection) to this
908    /// stream.
909    pub(crate) fn with_keepalive(mut self, guard: impl Send + 'static) -> Self {
910        self.keepalive = Some(Box::new(guard));
911        self
912    }
913}
914
915impl Stream for RowStream {
916    type Item = Result<Row, Error>;
917
918    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
919        let this = self.get_mut();
920        match this.rx.poll_recv(cx) {
921            // The query is over: release whatever is keeping the connection
922            // (a pooled checkout) now instead of waiting for `Drop`.
923            Poll::Ready(None) => {
924                this.keepalive.take();
925                Poll::Ready(None)
926            }
927            other => other,
928        }
929    }
930}
931
932impl Drop for RowStream {
933    fn drop(&mut self) {
934        // The connection keeps reading the rest of the result in the background
935        // (so it is never left half-read); if that takes too long, cancel the
936        // query server-side.
937        if !self.completed.load(Ordering::Acquire) {
938            cancel_when_stuck(&self.completed, self.cancel.take());
939        }
940    }
941}
942
943impl Actor {
944    async fn run(mut self, mut rx: mpsc::UnboundedReceiver<Request>) {
945        loop {
946            let broken = self.is_broken();
947            tokio::select! {
948                request = rx.recv() => {
949                    let Some(request) = request else { break };
950                    if broken || self.is_broken() {
951                        fail_request(request, Error::Closed);
952                        continue;
953                    }
954                    match request {
955                        Request::Query(query) => self.handle_query(query).await,
956                        Request::Script(script) => self.handle_script(script).await,
957                        Request::CopyIn(copy) => self.handle_copy_in(copy).await,
958                        Request::CopyOut(copy) => self.handle_copy_out(copy).await,
959                    }
960                    self.maybe_shrink_read_buf();
961                }
962                read = self.stream.read_buf(&mut self.read_buf), if !broken => {
963                    match read {
964                        Ok(0) => self.mark_broken(),
965                        Ok(_) => self.drain_notifications(),
966                        Err(_) => self.mark_broken(),
967                    }
968                }
969            }
970        }
971
972        // Say goodbye: closing the socket without `Terminate` makes the server
973        // log an unexpected EOF and can reset the connection mid-reply.
974        self.write_buf.clear();
975        let _ = frontend::terminate(&mut self.write_buf);
976        let _ = self.flush().await;
977    }
978
979    /// Releases an oversized read buffer once it is empty.
980    ///
981    /// `read_buf` grows to hold the largest message seen and never shrinks on
982    /// its own; on a long-lived pooled connection that would keep the memory
983    /// for the connection's whole lifetime (an RSS ratchet). Replacing it when
984    /// it is empty and large bounds that, while never dropping buffered bytes.
985    fn maybe_shrink_read_buf(&mut self) {
986        const SHRINK_THRESHOLD: usize = 1024 * 1024;
987        if self.read_buf.is_empty() && self.read_buf.capacity() > SHRINK_THRESHOLD {
988            self.read_buf = BytesMut::with_capacity(READ_BUFFER_SIZE);
989        }
990    }
991
992    /// Decodes and forwards any notifications sitting in the read buffer.
993    ///
994    /// Only asynchronous messages (`NotificationResponse`, `ParameterStatus`,
995    /// `NoticeResponse`) can arrive between requests. Anything else means the
996    /// stream is out of sync, and the connection is closed rather than having
997    /// its messages silently thrown away.
998    fn drain_notifications(&mut self) {
999        loop {
1000            match postgresql_protocol::decode_with_limit(&mut self.read_buf, self.max_message_len) {
1001                Ok(Some(BackendMessage::NotificationResponse {
1002                    pid,
1003                    channel,
1004                    payload,
1005                })) => {
1006                    let _ = self.notifications.send(Notification {
1007                        process_id: pid,
1008                        channel,
1009                        payload,
1010                    });
1011                }
1012                Ok(Some(BackendMessage::ParameterStatus {
1013                    ..
1014                }))
1015                | Ok(Some(BackendMessage::NoticeResponse(_))) => {}
1016                Ok(Some(other)) => {
1017                    tracing::warn!("postgresql: unexpected message while idle: {other:?}");
1018                    self.mark_broken();
1019                    return;
1020                }
1021                Ok(None) => break,
1022                Err(err) => {
1023                    tracing::warn!("postgresql: protocol error while idle: {err}");
1024                    self.mark_broken();
1025                    return;
1026                }
1027            }
1028        }
1029    }
1030
1031    /// Runs a multi-statement script via the simple query protocol.
1032    async fn handle_script(&mut self, request: ScriptRequest) {
1033        tracing::debug!(sql_len = request.sql.len(), "postgresql: script");
1034        self.write_buf.clear();
1035        if let Err(err) = frontend::query(&mut self.write_buf, &request.sql) {
1036            self.mark_broken();
1037            let _ = request.done.send(Err(err.into()));
1038            return;
1039        }
1040        if let Err(err) = self.flush().await {
1041            self.mark_broken();
1042            let _ = request.done.send(Err(err));
1043            return;
1044        }
1045
1046        let mut affected = 0u64;
1047        let mut pending_error: Option<Error> = None;
1048        loop {
1049            match self.read_message().await {
1050                Ok(BackendMessage::CommandComplete(tag)) => affected = parse_command_tag(&tag),
1051                Ok(BackendMessage::ReadyForQuery(_)) => break,
1052                Ok(BackendMessage::RowDescription(_)) => {}
1053                Ok(BackendMessage::DataRow(_)) => {}
1054                Ok(BackendMessage::NoticeResponse(_)) => {}
1055                Ok(BackendMessage::EmptyQueryResponse) => {}
1056                // A script cannot stream `COPY` data: leave COPY mode so the
1057                // connection is not wedged, then report the error.
1058                Ok(BackendMessage::CopyInResponse {
1059                    ..
1060                }) => {
1061                    pending_error = Some(self.abort_unexpected_copy(true, false).await);
1062                    break;
1063                }
1064                Ok(BackendMessage::CopyOutResponse {
1065                    ..
1066                }) => {
1067                    pending_error = Some(self.abort_unexpected_copy(false, false).await);
1068                    break;
1069                }
1070                Ok(_) => {}
1071                Err(Error::Server(db)) => pending_error = Some(Error::Server(db)),
1072                Err(err) => {
1073                    self.mark_broken();
1074                    let _ = request.done.send(Err(err));
1075                    return;
1076                }
1077            }
1078        }
1079
1080        let _ = request.done.send(match pending_error {
1081            Some(err) => Err(err),
1082            None => Ok(affected),
1083        });
1084    }
1085
1086    async fn handle_copy_in(&mut self, request: CopyInRequest) {
1087        tracing::debug!(sql = %request.sql, "postgresql: copy in");
1088        self.write_buf.clear();
1089        let mut encode_error = frontend::parse(&mut self.write_buf, "", &request.sql, &[]).err();
1090        if encode_error.is_none() {
1091            match frontend::bind_start(&mut self.write_buf, "", "", 0) {
1092                Ok(len_pos) => {
1093                    encode_error = frontend::bind_finish(&mut self.write_buf, Format::Binary, len_pos)
1094                        .and_then(|()| frontend::execute(&mut self.write_buf, "", 0))
1095                        .err();
1096                }
1097                Err(err) => encode_error = Some(err),
1098            }
1099        }
1100        if let Some(err) = encode_error {
1101            self.mark_broken();
1102            let _ = request.done.send(Err(err.into()));
1103            return;
1104        }
1105        if let Err(err) = self.flush().await {
1106            self.mark_broken();
1107            let _ = request.done.send(Err(err));
1108            return;
1109        }
1110
1111        let mut pending_error: Option<Box<DbError>> = None;
1112        loop {
1113            match self.read_message().await {
1114                Ok(BackendMessage::ParseComplete) => {}
1115                Ok(BackendMessage::BindComplete) => {}
1116                Ok(BackendMessage::CopyInResponse {
1117                    ..
1118                }) => break,
1119                Ok(BackendMessage::NoticeResponse(_)) => {}
1120                Ok(_) => {}
1121                Err(Error::Server(db)) => {
1122                    pending_error = Some(db);
1123                    break;
1124                }
1125                Err(err) => {
1126                    self.mark_broken();
1127                    let _ = request.done.send(Err(err));
1128                    return;
1129                }
1130            }
1131        }
1132
1133        if let Some(db) = pending_error {
1134            let _ = self.sync_and_drain().await;
1135            let _ = request.done.send(Err(Error::Server(db)));
1136            return;
1137        }
1138
1139        let mut chunks = request.chunks;
1140        let mut stream_error: Option<Error> = None;
1141        let mut finished = false;
1142        while let Some(chunk) = chunks.recv().await {
1143            self.write_buf.clear();
1144            match chunk {
1145                CopyInChunk::Data(data) => {
1146                    if let Err(err) = frontend::copy_data(&mut self.write_buf, &data) {
1147                        stream_error = Some(err.into());
1148                        break;
1149                    }
1150                }
1151                CopyInChunk::Done => {
1152                    if let Err(err) = frontend::copy_done(&mut self.write_buf) {
1153                        stream_error = Some(err.into());
1154                        break;
1155                    }
1156                    finished = true;
1157                    if let Err(err) = self.flush().await {
1158                        self.mark_broken();
1159                        let _ = request.done.send(Err(err));
1160                        return;
1161                    }
1162                    break;
1163                }
1164                CopyInChunk::Fail(message) => {
1165                    if let Err(err) = frontend::copy_fail(&mut self.write_buf, &message) {
1166                        stream_error = Some(err.into());
1167                        break;
1168                    }
1169                    finished = true;
1170                    if let Err(err) = self.flush().await {
1171                        self.mark_broken();
1172                        let _ = request.done.send(Err(err));
1173                        return;
1174                    }
1175                    break;
1176                }
1177            }
1178            if let Err(err) = self.flush().await {
1179                self.mark_broken();
1180                let _ = request.done.send(Err(err));
1181                return;
1182            }
1183        }
1184
1185        // If we did not send `CopyDone` or `CopyFail`, leave COPY-in mode
1186        // explicitly: the server otherwise waits for more data forever and
1187        // every later query on this connection hangs. This also covers the
1188        // error paths above, which must not send a bare `Sync` while the
1189        // server is still in COPY mode.
1190        if !finished {
1191            self.write_buf.clear();
1192            let message = if stream_error.is_some() {
1193                "COPY aborted by the client"
1194            } else {
1195                "COPY writer was dropped before completion"
1196            };
1197            if let Err(err) = frontend::copy_fail(&mut self.write_buf, message) {
1198                self.mark_broken();
1199                let _ = request.done.send(Err(err.into()));
1200                return;
1201            }
1202            if let Err(err) = self.flush().await {
1203                self.mark_broken();
1204                let _ = request.done.send(Err(err));
1205                return;
1206            }
1207        }
1208
1209        let mut result = self.sync_and_drain().await;
1210        if let Some(err) = stream_error {
1211            result = Err(err);
1212        }
1213        let _ = request.done.send(result);
1214    }
1215
1216    async fn handle_copy_out(&mut self, request: CopyOutRequest) {
1217        tracing::debug!(sql = %request.sql, "postgresql: copy out");
1218        self.write_buf.clear();
1219        let mut encode_error = frontend::parse(&mut self.write_buf, "", &request.sql, &[]).err();
1220        if encode_error.is_none() {
1221            match frontend::bind_start(&mut self.write_buf, "", "", 0) {
1222                Ok(len_pos) => {
1223                    encode_error = frontend::bind_finish(&mut self.write_buf, Format::Binary, len_pos)
1224                        .and_then(|()| frontend::execute(&mut self.write_buf, "", 0))
1225                        .and_then(|()| frontend::sync(&mut self.write_buf))
1226                        .err();
1227                }
1228                Err(err) => encode_error = Some(err),
1229            }
1230        }
1231        if let Some(err) = encode_error {
1232            self.mark_broken();
1233            let _ = request.done.send(Err(err.into()));
1234            return;
1235        }
1236        if let Err(err) = self.flush().await {
1237            self.mark_broken();
1238            let _ = request.done.send(Err(err));
1239            return;
1240        }
1241
1242        let mut pending_error: Option<Error> = None;
1243        loop {
1244            match self.read_message().await {
1245                Ok(BackendMessage::CopyData(data)) => {
1246                    // Bounded channel: a slow consumer applies backpressure.
1247                    if request.chunks.send(Ok(data)).await.is_err() {
1248                        // The consumer is gone; keep draining to `ReadyForQuery`
1249                        // so the connection stays usable.
1250                        continue;
1251                    }
1252                }
1253                Ok(BackendMessage::ReadyForQuery(_)) => break,
1254                Ok(BackendMessage::NoticeResponse(_)) => {}
1255                Ok(_) => {}
1256                Err(Error::Server(db)) => {
1257                    pending_error = Some(Error::Server(db));
1258                }
1259                Err(err) => {
1260                    self.mark_broken();
1261                    pending_error = Some(err);
1262                    break;
1263                }
1264            }
1265        }
1266
1267        match pending_error {
1268            // The stream is what the caller reads, so it gets the real error;
1269            // `done` is only awaited for the row count.
1270            Some(Error::Server(db)) => {
1271                let _ = request.chunks.send(Err(Error::Server(db.clone()))).await;
1272                let _ = request.done.send(Err(Error::Server(db)));
1273            }
1274            Some(err) => {
1275                let _ = request.chunks.send(Err(Error::Closed)).await;
1276                let _ = request.done.send(Err(err));
1277            }
1278            None => {
1279                let _ = request.done.send(Ok(0));
1280            }
1281        }
1282    }
1283
1284    /// Sends `Sync` and reads until `ReadyForQuery`, returning the affected
1285    /// row count (or the server error).
1286    async fn sync_and_drain(&mut self) -> Result<u64, Error> {
1287        self.write_buf.clear();
1288        if let Err(err) = frontend::sync(&mut self.write_buf) {
1289            self.mark_broken();
1290            return Err(err.into());
1291        }
1292        if let Err(err) = self.flush().await {
1293            self.mark_broken();
1294            return Err(err);
1295        }
1296        let mut affected = 0;
1297        let mut result = Ok(affected);
1298        loop {
1299            match self.read_message().await {
1300                Ok(BackendMessage::CommandComplete(tag)) => {
1301                    affected = parse_command_tag(&tag);
1302                }
1303                Ok(BackendMessage::ReadyForQuery(_)) => break,
1304                Ok(BackendMessage::NoticeResponse(_)) => {}
1305                Ok(_) => {}
1306                Err(Error::Server(db)) => {
1307                    result = Err(Error::Server(db));
1308                }
1309                Err(err) => {
1310                    self.mark_broken();
1311                    result = Err(err);
1312                    break;
1313                }
1314            }
1315        }
1316        match result {
1317            Ok(_) => Ok(affected),
1318            Err(err) => Err(err),
1319        }
1320    }
1321
1322    async fn handle_query(&mut self, query: QueryRequest) {
1323        tracing::debug!(
1324            sql = %query.sql,
1325            params = query.params.oids.len(),
1326            "postgresql: execute"
1327        );
1328
1329        let mut attempts = 0u8;
1330        let result = loop {
1331            attempts += 1;
1332            match self.attempt_query(&query).await {
1333                Ok(affected) => break Ok(affected),
1334                Err(AttemptError {
1335                    error,
1336                    rows_sent,
1337                }) => {
1338                    // A stale plan can only be retried when no row was already
1339                    // delivered: re-running the query would send them twice.
1340                    let retryable = should_retry(attempts, rows_sent, &error);
1341                    if retryable {
1342                        let hash = statement_key(&query.sql, query.params.oids.as_slice());
1343                        if let Some(cached) = self.cache.swap_remove(&hash) {
1344                            self.pending_closes.push(cached.name);
1345                        }
1346                        tracing::debug!("postgresql: re-preparing statement after a stale plan");
1347                        continue;
1348                    }
1349                    break Err(error);
1350                }
1351            }
1352        };
1353
1354        // Mark complete first so a dropped `RowStream` does not cancel a query
1355        // that has already finished.
1356        query.completed.store(true, Ordering::Release);
1357        match result {
1358            Ok(affected) => {
1359                let _ = query.done.send(Ok(affected));
1360            }
1361            Err(err) => {
1362                if !query.rows.is_closed() {
1363                    // The row stream is what the caller reads; `done` is only
1364                    // awaited by `execute`, which drops the row channel. Both
1365                    // want the error, so the rows get a description of it.
1366                    let for_rows = match &err {
1367                        Error::Server(db) => Error::Server(db.clone()),
1368                        Error::Closed => Error::Closed,
1369                        other => Error::ConnectionFailed(other.to_string()),
1370                    };
1371                    let _ = query.rows.send(Err(for_rows)).await;
1372                }
1373                let _ = query.done.send(Err(err));
1374            }
1375        }
1376    }
1377
1378    /// Performs one attempt: prepare (if needed), bind, execute and read the
1379    /// result. Rows are streamed to `query.rows`; an error carries whether any
1380    /// row was already delivered, so a stale plan can only be retried when the
1381    /// caller's stream has seen nothing yet.
1382    async fn attempt_query(&mut self, query: &QueryRequest) -> Result<u64, AttemptError> {
1383        let hash = statement_key(&query.sql, query.params.oids.as_slice());
1384
1385        let (name, fields): (Arc<str>, Arc<Columns>) = if self.statement_cache_size == 0 {
1386            // Caching disabled (e.g. transaction-mode pooler): use the unnamed
1387            // statement.
1388            let fields = self.prepare("", &query.sql, query.params.oids.as_slice()).await?;
1389            (Arc::from(""), fields)
1390        } else {
1391            let cached_matches = self
1392                .cache
1393                .get(&hash)
1394                .map(|cached| cached.matches(&query.sql, query.params.oids.as_slice()))
1395                .unwrap_or(false);
1396            if cached_matches {
1397                // Move the entry to the back of the map in O(1): `swap_remove`
1398                // followed by `insert` is an approximate LRU and avoids the
1399                // O(cache size) scan a `VecDeque::retain` would cost on every
1400                // cache hit.
1401                let (_, cached) = self.cache.swap_remove_entry(&hash).expect("checked");
1402                let name = cached.name.clone();
1403                let fields = cached.fields.clone();
1404                self.cache.insert(hash, cached);
1405                (name, fields)
1406            } else {
1407                self.statement_counter += 1;
1408                let name: Arc<str> = Arc::from(format!("s{}", self.statement_counter));
1409                let fields = self.prepare(&name, &query.sql, query.params.oids.as_slice()).await?;
1410                let entry = CachedStatement {
1411                    sql: query.sql.clone(),
1412                    oids: query.params.oids.clone(),
1413                    name: name.clone(),
1414                    fields: fields.clone(),
1415                };
1416                // A hash collision displaces an unrelated statement; close it so
1417                // it is not leaked server-side.
1418                if let Some(old) = self.cache.insert(hash, entry) {
1419                    self.pending_closes.push(old.name);
1420                }
1421                self.evict_statements();
1422                (name, fields)
1423            }
1424        };
1425
1426        let param_count = query.params.oids.len();
1427        self.write_buf.clear();
1428        self.write_pending_closes()?;
1429        let len_pos = frontend::bind_start(&mut self.write_buf, "", &name, param_count)?;
1430        self.write_buf.extend_from_slice(&query.params.values);
1431        frontend::bind_finish(&mut self.write_buf, Format::Binary, len_pos)?;
1432        frontend::execute(&mut self.write_buf, "", query.max_rows)?;
1433        frontend::sync(&mut self.write_buf)?;
1434
1435        if let Err(err) = self.flush().await {
1436            self.mark_broken();
1437            return Err(AttemptError::before_rows(err));
1438        }
1439
1440        let mut pending_error: Option<Box<DbError>> = None;
1441        let mut affected = 0u64;
1442        let mut rows_sent = false;
1443
1444        loop {
1445            match self.read_message().await {
1446                Ok(BackendMessage::BindComplete) => {}
1447                Ok(BackendMessage::DataRow(payload)) => {
1448                    rows_sent = true;
1449                    if !query.rows.is_closed() {
1450                        match Row::parse(fields.clone(), &payload, self.zone) {
1451                            Ok(row) => {
1452                                // Bounded channel: this applies backpressure.
1453                                let _ = query.rows.send(Ok(row)).await;
1454                            }
1455                            Err(err) => {
1456                                let _ = query.rows.send(Err(err)).await;
1457                            }
1458                        }
1459                    }
1460                }
1461                Ok(BackendMessage::CommandComplete(tag)) => {
1462                    affected = parse_command_tag(&tag);
1463                }
1464                Ok(BackendMessage::NoticeResponse(_notice)) => {}
1465                Ok(BackendMessage::EmptyQueryResponse) => {}
1466                // The row limit was reached; the trailing `Sync` closes the
1467                // portal, so keep reading to `ReadyForQuery`.
1468                Ok(BackendMessage::PortalSuspended) => {}
1469                // The server entered COPY mode for a statement run through the
1470                // parameterized path. Leave COPY mode (a bare `COPY ... FROM
1471                // STDIN` would otherwise make every later query hang), report
1472                // it, and let `copy_in!` / `copy_out!` be the supported paths.
1473                Ok(BackendMessage::CopyInResponse {
1474                    ..
1475                }) => {
1476                    let error = self.abort_unexpected_copy(true, true).await;
1477                    return Err(AttemptError {
1478                        error,
1479                        rows_sent,
1480                    });
1481                }
1482                Ok(BackendMessage::CopyOutResponse {
1483                    ..
1484                }) => {
1485                    let error = self.abort_unexpected_copy(false, true).await;
1486                    return Err(AttemptError {
1487                        error,
1488                        rows_sent,
1489                    });
1490                }
1491                Ok(BackendMessage::ReadyForQuery(_)) => break,
1492                Ok(_) => {}
1493                Err(Error::Server(db)) => {
1494                    tracing::debug!(code = %db.code, message = %db.message, "postgresql: server error");
1495                    pending_error = Some(db);
1496                }
1497                Err(err) => {
1498                    self.mark_broken();
1499                    return Err(AttemptError {
1500                        error: err,
1501                        rows_sent,
1502                    });
1503                }
1504            }
1505        }
1506
1507        match pending_error {
1508            Some(db) => Err(AttemptError {
1509                error: Error::Server(db),
1510                rows_sent,
1511            }),
1512            None => Ok(affected),
1513        }
1514    }
1515
1516    /// Recovers the connection after the server unexpectedly entered COPY mode
1517    /// for a statement that is not a `copy_in!` / `copy_out!` operation.
1518    ///
1519    /// For COPY-in a `CopyFail` is sent — otherwise the server keeps waiting
1520    /// for data and every later query on the connection hangs. When the
1521    /// statement was run through the extended protocol (`extended`), a fresh
1522    /// `Sync` follows: the backend discards frontend messages until a `Sync`
1523    /// after a copy-in error, and the `Sync` that accompanied `Execute` was
1524    /// consumed when COPY mode was entered. Draining then reaches
1525    /// `ReadyForQuery` and the connection is reusable. Returns the error to
1526    /// report to the caller.
1527    async fn abort_unexpected_copy(&mut self, copy_in: bool, extended: bool) -> Error {
1528        if copy_in {
1529            self.write_buf.clear();
1530            let encode =
1531                frontend::copy_fail(&mut self.write_buf, "COPY ... FROM STDIN is not supported here; use copy_in!")
1532                    .and_then(|()| {
1533                        if extended {
1534                            frontend::sync(&mut self.write_buf)
1535                        } else {
1536                            Ok(())
1537                        }
1538                    });
1539            if let Err(err) = encode {
1540                self.mark_broken();
1541                return err.into();
1542            }
1543            if let Err(err) = self.flush().await {
1544                self.mark_broken();
1545                return err;
1546            }
1547        }
1548        loop {
1549            match self.read_message().await {
1550                Ok(BackendMessage::ReadyForQuery(_)) => break,
1551                Ok(_) => {}
1552                Err(Error::Server(_)) => {}
1553                Err(err) => {
1554                    self.mark_broken();
1555                    return err;
1556                }
1557            }
1558        }
1559        Error::Decode(
1560            if copy_in {
1561                "COPY ... FROM STDIN cannot be run as a parameterized query; use copy_in!"
1562            } else {
1563                "COPY ... TO STDOUT cannot be run as a parameterized query; use copy_out!"
1564            }
1565            .into(),
1566        )
1567    }
1568
1569    /// Evicts the least recently used statements beyond the cache capacity and
1570    /// queues a `Close` for each of them.
1571    ///
1572    /// The `Close` messages are written into the next request, so eviction
1573    /// costs no round trip of its own (the `CloseComplete` replies are simply
1574    /// skipped by the response loop).
1575    fn evict_statements(&mut self) {
1576        if self.statement_cache_size == 0 {
1577            return;
1578        }
1579        while self.cache.len() > self.statement_cache_size {
1580            let Some((_, cached)) = self.cache.swap_remove_index(0) else {
1581                break;
1582            };
1583            self.pending_closes.push(cached.name);
1584        }
1585    }
1586
1587    /// Appends the queued `Close` messages to the write buffer.
1588    fn write_pending_closes(&mut self) -> Result<(), Error> {
1589        for name in self.pending_closes.drain(..) {
1590            frontend::close(&mut self.write_buf, b'S', &name)?;
1591        }
1592        Ok(())
1593    }
1594
1595    async fn prepare(&mut self, name: &str, sql: &str, param_oids: &[Oid]) -> Result<Arc<Columns>, Error> {
1596        self.write_buf.clear();
1597        self.write_pending_closes()?;
1598        frontend::parse(&mut self.write_buf, name, sql, param_oids)?;
1599        frontend::describe(&mut self.write_buf, b'S', name)?;
1600        frontend::sync(&mut self.write_buf)?;
1601        self.flush().await?;
1602
1603        let mut fields: Option<Vec<postgresql_protocol::backend::FieldDescription>> = None;
1604        let mut pending_error: Option<Box<DbError>> = None;
1605        loop {
1606            match self.read_message().await {
1607                Ok(BackendMessage::ParseComplete) => {}
1608                Ok(BackendMessage::ParameterDescription(_)) => {}
1609                Ok(BackendMessage::RowDescription(described)) => fields = Some(described),
1610                Ok(BackendMessage::NoData) => fields = Some(Vec::new()),
1611                Ok(BackendMessage::NoticeResponse(_)) => {}
1612                Ok(BackendMessage::ReadyForQuery(_)) => break,
1613                Ok(_) => {}
1614                Err(Error::Server(db)) => pending_error = Some(db),
1615                Err(err) => return Err(err),
1616            }
1617        }
1618
1619        if let Some(db) = pending_error {
1620            return Err(Error::Server(db));
1621        }
1622
1623        Ok(Columns::from_fields(&fields.unwrap_or_default()))
1624    }
1625
1626    async fn flush(&mut self) -> Result<(), Error> {
1627        self.stream.write_all(&self.write_buf).await.map_err(Error::Io)?;
1628        self.stream.flush().await.map_err(Error::Io)?;
1629        self.write_buf.clear();
1630        Ok(())
1631    }
1632
1633    async fn read_message(&mut self) -> Result<BackendMessage, Error> {
1634        loop {
1635            match postgresql_protocol::decode_with_limit(&mut self.read_buf, self.max_message_len) {
1636                // Asynchronous notifications can arrive at any time; forward
1637                // them and re-decode the buffer.
1638                Ok(Some(BackendMessage::NotificationResponse {
1639                    pid,
1640                    channel,
1641                    payload,
1642                })) => {
1643                    let _ = self.notifications.send(Notification {
1644                        process_id: pid,
1645                        channel,
1646                        payload,
1647                    });
1648                    continue;
1649                }
1650                Ok(Some(message)) => return Ok(message),
1651                Ok(None) => {}
1652                Err(ProtocolError::Server(db)) => return Err(Error::Server(db)),
1653                Err(err) => return Err(Error::Protocol(err)),
1654            }
1655            self.read_buf.reserve(READ_BUFFER_SIZE);
1656            let read = self.stream.read_buf(&mut self.read_buf).await.map_err(Error::Io)?;
1657            if read == 0 {
1658                return Err(Error::Closed);
1659            }
1660        }
1661    }
1662}
1663
1664fn fail_request(request: Request, err: Error) {
1665    match request {
1666        Request::Query(query) => {
1667            let _ = query.done.send(Err(err));
1668        }
1669        Request::Script(script) => {
1670            let _ = script.done.send(Err(err));
1671        }
1672        Request::CopyIn(copy) => {
1673            let _ = copy.done.send(Err(err));
1674        }
1675        Request::CopyOut(copy) => {
1676            let _ = copy.done.send(Err(err));
1677        }
1678    }
1679}
1680
1681fn parse_command_tag(tag: &Bytes) -> u64 {
1682    let Ok(text) = std::str::from_utf8(tag) else {
1683        return 0;
1684    };
1685    text.rsplit(' ').next().and_then(|n| n.parse::<u64>().ok()).unwrap_or(0)
1686}
1687
1688async fn establish(config: &Config) -> Result<(PgStream, BytesMut, i32, i32), Error> {
1689    let address = format!("{}:{}", config.host, config.port);
1690    let tcp = tokio::time::timeout(config.connect_timeout, TcpStream::connect(&address))
1691        .await
1692        .map_err(|_| Error::Config(format!("timed out connecting to {address}")))?
1693        .map_err(Error::Io)?;
1694    let _ = tcp.set_nodelay(true);
1695
1696    let mut stream = negotiate_tls(tcp, config).await?;
1697
1698    let mut params: SmallVec<(&str, &str), 5> = SmallVec::new();
1699    params.push(("client_encoding", "UTF8"));
1700    params.push(("user", &config.user));
1701    if let Some(database) = &config.database {
1702        params.push(("database", database));
1703    }
1704    if let Some(app) = &config.application_name {
1705        params.push(("application_name", app));
1706    }
1707    if let Some(options) = &config.options {
1708        params.push(("options", options));
1709    }
1710
1711    let mut write_buf = BytesMut::with_capacity(256);
1712    frontend::startup(&mut write_buf, &params)?;
1713    stream.write_all(&write_buf).await.map_err(Error::Io)?;
1714    stream.flush().await.map_err(Error::Io)?;
1715
1716    let mut read_buf = BytesMut::with_capacity(READ_BUFFER_SIZE);
1717    let (process_id, secret_key) = authenticate(&mut stream, &mut read_buf, config).await?;
1718    tracing::debug!(
1719        host = %config.host,
1720        port = config.port,
1721        user = %config.user,
1722        "postgresql: connected"
1723    );
1724    Ok((stream, read_buf, process_id, secret_key))
1725}
1726
1727async fn negotiate_tls(tcp: TcpStream, config: &Config) -> Result<PgStream, Error> {
1728    let mut tcp = tcp;
1729    let mut request = BytesMut::new();
1730    frontend::ssl_request(&mut request);
1731    tcp.write_all(&request).await.map_err(Error::Io)?;
1732    tcp.flush().await.map_err(Error::Io)?;
1733
1734    let mut response = [0u8; 1];
1735    tcp.read_exact(&mut response).await.map_err(Error::Io)?;
1736
1737    match response[0] {
1738        b'S' => {
1739            let connector = crate::stream::tls::connector(config)?;
1740            let server_name = rustls::pki_types::ServerName::try_from(config.host.clone())
1741                .map_err(|_| Error::Config(format!("invalid TLS server name `{}`", config.host)))?;
1742            let stream = connector.connect(server_name, tcp).await.map_err(Error::Io)?;
1743            Ok(PgStream::Tls(Box::new(stream)))
1744        }
1745        b'N' => Err(Error::Config(
1746            "the server does not support TLS; this client always requires TLS".into(),
1747        )),
1748        other => Err(Error::Protocol(ProtocolError::Protocol(format!(
1749            "unexpected response to SSLRequest: {other}"
1750        )))),
1751    }
1752}
1753
1754async fn authenticate(stream: &mut PgStream, read_buf: &mut BytesMut, config: &Config) -> Result<(i32, i32), Error> {
1755    let mut write_buf = BytesMut::with_capacity(256);
1756    let mut integer_datetimes: Option<String> = None;
1757    let mut backend_key: Option<(i32, i32)> = None;
1758    let mut messages = 0usize;
1759
1760    loop {
1761        messages += 1;
1762        if messages > MAX_AUTH_MESSAGES {
1763            return Err(Error::Protocol(ProtocolError::Protocol(format!(
1764                "the server sent more than {MAX_AUTH_MESSAGES} messages during authentication"
1765            ))));
1766        }
1767        let message = read_backend(stream, read_buf, config.max_message_len).await?;
1768        match message {
1769            BackendMessage::AuthenticationOk => {}
1770            BackendMessage::AuthenticationCleartextPassword => {
1771                let password = config.password.as_deref().unwrap_or("");
1772                write_buf.clear();
1773                frontend::password(&mut write_buf, password)?;
1774                stream.write_all(&write_buf).await.map_err(Error::Io)?;
1775                stream.flush().await.map_err(Error::Io)?;
1776            }
1777            BackendMessage::AuthenticationSasl(mechanisms) => {
1778                if !mechanisms.iter().any(|m| m == "SCRAM-SHA-256") {
1779                    return Err(Error::Server(Box::new(DbError {
1780                        severity: "FATAL".into(),
1781                        code: Default::default(),
1782                        message: "server does not offer SCRAM-SHA-256 authentication".into(),
1783                        ..DbError::default()
1784                    })));
1785                }
1786                let mut client = ScramClient::with_max_iterations(
1787                    config.password.as_deref().unwrap_or(""),
1788                    config.max_scram_iterations,
1789                )?;
1790                write_buf.clear();
1791                frontend::sasl_initial_response(
1792                    &mut write_buf,
1793                    "SCRAM-SHA-256",
1794                    client.client_first_message().as_bytes(),
1795                )?;
1796                stream.write_all(&write_buf).await.map_err(Error::Io)?;
1797                stream.flush().await.map_err(Error::Io)?;
1798
1799                let continue_message = read_backend(stream, read_buf, config.max_message_len).await?;
1800                match continue_message {
1801                    BackendMessage::AuthenticationSaslContinue(data) => {
1802                        // The PBKDF2 key derivation is pure CPU work and can
1803                        // take hundreds of milliseconds (the server picks the
1804                        // iteration count); run it on the blocking pool so it
1805                        // does not stall an async worker thread.
1806                        let (returned, final_message) = tokio::task::spawn_blocking(move || {
1807                            client.parse_server_first_message(&data)?;
1808                            let final_message = client.build_client_final_message()?;
1809                            Ok::<_, ProtocolError>((client, final_message))
1810                        })
1811                        .await
1812                        .map_err(|_| Error::Closed)??;
1813                        client = returned;
1814                        write_buf.clear();
1815                        frontend::sasl_response(&mut write_buf, &final_message)?;
1816                        stream.write_all(&write_buf).await.map_err(Error::Io)?;
1817                        stream.flush().await.map_err(Error::Io)?;
1818                    }
1819                    other => {
1820                        return Err(Error::Protocol(ProtocolError::Protocol(format!(
1821                            "expected SASLContinue, got {other:?}"
1822                        ))));
1823                    }
1824                }
1825
1826                let final_message = read_backend(stream, read_buf, config.max_message_len).await?;
1827                match final_message {
1828                    BackendMessage::AuthenticationSaslFinal(data) => {
1829                        // Verifying the server signature is a couple of HMACs;
1830                        // keep it off the worker thread for consistency.
1831                        tokio::task::spawn_blocking(move || client.parse_server_final_message(&data))
1832                            .await
1833                            .map_err(|_| Error::Closed)??;
1834                    }
1835                    other => {
1836                        return Err(Error::Protocol(ProtocolError::Protocol(format!(
1837                            "expected SASLFinal, got {other:?}"
1838                        ))));
1839                    }
1840                }
1841            }
1842            BackendMessage::AuthenticationMd5Password(_) => {
1843                return Err(Error::Server(Box::new(DbError {
1844                    severity: "FATAL".into(),
1845                    code: Default::default(),
1846                    message: "MD5 authentication is not supported; configure the server to use SCRAM-SHA-256".into(),
1847                    ..DbError::default()
1848                })));
1849            }
1850            BackendMessage::ParameterStatus {
1851                name,
1852                value,
1853            } => {
1854                if name == "integer_datetimes" {
1855                    integer_datetimes = Some(value);
1856                }
1857            }
1858            BackendMessage::BackendKeyData {
1859                pid,
1860                secret_key,
1861            } => {
1862                backend_key = Some((pid, secret_key));
1863            }
1864            BackendMessage::NoticeResponse(_) => {}
1865            BackendMessage::ReadyForQuery(_) => break,
1866            other => {
1867                return Err(Error::Protocol(ProtocolError::Protocol(format!(
1868                    "unexpected message during startup: {other:?}"
1869                ))));
1870            }
1871        }
1872    }
1873
1874    if let Some(setting) = integer_datetimes {
1875        if setting != "on" {
1876            return Err(Error::Config(
1877                "the server reports integer_datetimes=off, which is not supported".into(),
1878            ));
1879        }
1880    }
1881
1882    Ok(backend_key.unwrap_or((0, 0)))
1883}
1884
1885async fn read_backend(
1886    stream: &mut PgStream,
1887    buf: &mut BytesMut,
1888    max_message_len: usize,
1889) -> Result<BackendMessage, Error> {
1890    loop {
1891        match postgresql_protocol::decode_with_limit(buf, max_message_len) {
1892            Ok(Some(message)) => return Ok(message),
1893            Ok(None) => {}
1894            Err(ProtocolError::Server(db)) => return Err(Error::Server(db)),
1895            Err(err) => return Err(Error::Protocol(err)),
1896        }
1897        buf.reserve(READ_BUFFER_SIZE);
1898        let read = stream.read_buf(buf).await.map_err(Error::Io)?;
1899        if read == 0 {
1900            return Err(Error::Closed);
1901        }
1902    }
1903}
1904
1905impl crate::query::sealed::Sealed for Connection {}
1906
1907impl crate::query::Capable for Connection {
1908    fn execute<'a>(
1909        &'a self,
1910        sql: &'a str,
1911        params: &'a [&'a dyn ToSql],
1912    ) -> impl std::future::Future<Output = Result<u64, Error>> + Send + 'a {
1913        async move { Connection::execute(self, sql, params).await }
1914    }
1915
1916    fn fetch_rows<'a>(
1917        &'a self,
1918        sql: &'a str,
1919        params: &'a [&'a dyn ToSql],
1920        max_rows: i32,
1921    ) -> impl std::future::Future<Output = Result<RowStream, Error>> + Send + 'a {
1922        async move { Connection::fetch_rows(self, sql, params, max_rows).await }
1923    }
1924}
1925
1926impl crate::query::ScriptExecutor for Connection {
1927    fn execute_script<'a>(&'a self, sql: &'a str) -> impl std::future::Future<Output = Result<u64, Error>> + Send + 'a {
1928        async move { Connection::execute_script(self, sql).await }
1929    }
1930}
1931
1932#[cfg(test)]
1933mod tests {
1934    use super::*;
1935
1936    #[test]
1937    fn stale_plan_is_only_retried_before_rows_are_delivered() {
1938        let stale = Error::Server(Box::new(DbError {
1939            code: "0A000".into(),
1940            ..DbError::default()
1941        }));
1942        assert!(should_retry(1, false, &stale));
1943        assert!(
1944            !should_retry(1, true, &stale),
1945            "retrying after rows were delivered would duplicate them"
1946        );
1947        assert!(!should_retry(2, false, &stale), "only one retry is allowed");
1948
1949        let missing = Error::Server(Box::new(DbError {
1950            code: "26000".into(),
1951            ..DbError::default()
1952        }));
1953        assert!(should_retry(1, false, &missing));
1954
1955        let other = Error::Server(Box::new(DbError {
1956            code: "23505".into(),
1957            ..DbError::default()
1958        }));
1959        assert!(!should_retry(1, false, &other));
1960
1961        assert!(!should_retry(1, false, &Error::Closed));
1962    }
1963}