Skip to main content

sqlmodel_postgres/
async_connection.rs

1//! Async PostgreSQL connection implementation.
2//!
3//! This module implements an async PostgreSQL connection using asupersync's TCP
4//! primitives. It provides a shared wrapper that implements `sqlmodel-core`'s
5//! [`Connection`] trait.
6//!
7//! The implementation currently focuses on:
8//! - Async connect + authentication (cleartext, MD5, SCRAM-SHA-256)
9//! - Extended query protocol for parameterized queries
10//! - Row decoding via the postgres type registry (OID + text/binary format)
11//! - Basic transaction support (BEGIN/COMMIT/ROLLBACK + savepoints)
12
13// Allow `impl Future` return types in trait methods - intentional for async trait compat
14#![allow(clippy::manual_async_fn)]
15// The Error type is intentionally large to carry full context
16#![allow(clippy::result_large_err)]
17
18use std::collections::HashMap;
19use std::future::Future;
20#[cfg(feature = "tls")]
21use std::io::{Read, Write};
22use std::sync::Arc;
23
24use asupersync::io::{AsyncRead, AsyncWrite, ReadBuf};
25use asupersync::net::TcpStream;
26use asupersync::sync::{Mutex, OwnedMutexGuard};
27use asupersync::{Cx, Outcome};
28
29use sqlmodel_core::connection::{Connection, IsolationLevel, PreparedStatement, TransactionOps};
30use sqlmodel_core::error::{
31    ConnectionError, ConnectionErrorKind, ProtocolError, QueryError, QueryErrorKind,
32};
33use sqlmodel_core::row::ColumnInfo;
34use sqlmodel_core::{Error, Row, Value};
35
36use crate::auth::ScramClient;
37use crate::config::{PgConfig, SslMode};
38use crate::connection::{ConnectionState, TransactionStatusState};
39use crate::protocol::{
40    BackendMessage, DescribeKind, ErrorFields, FrontendMessage, MessageReader, MessageWriter,
41    PROTOCOL_VERSION,
42};
43use crate::types::{Format, decode_value, encode_value};
44
45#[cfg(feature = "tls")]
46use crate::tls;
47
48// A TLS stream is ~1KB vs ~72B for plain TCP; boxing it would add a
49// pointer chase on every read/write of the hot I/O path, so keep it inline.
50#[allow(clippy::large_enum_variant)]
51enum PgAsyncStream {
52    Plain(TcpStream),
53    #[cfg(feature = "tls")]
54    Tls(AsyncTlsStream),
55    #[cfg(feature = "tls")]
56    Closed,
57}
58
59impl PgAsyncStream {
60    #[cfg(feature = "tls")]
61    async fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
62        match self {
63            PgAsyncStream::Plain(s) => read_exact_plain_async(s, buf).await,
64            #[cfg(feature = "tls")]
65            PgAsyncStream::Tls(s) => s.read_exact(buf).await,
66            #[cfg(feature = "tls")]
67            PgAsyncStream::Closed => Err(std::io::Error::new(
68                std::io::ErrorKind::NotConnected,
69                "connection closed",
70            )),
71        }
72    }
73
74    async fn read_some(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
75        match self {
76            PgAsyncStream::Plain(s) => read_some_plain_async(s, buf).await,
77            #[cfg(feature = "tls")]
78            PgAsyncStream::Tls(s) => s.read_plain(buf).await,
79            #[cfg(feature = "tls")]
80            PgAsyncStream::Closed => Err(std::io::Error::new(
81                std::io::ErrorKind::NotConnected,
82                "connection closed",
83            )),
84        }
85    }
86
87    async fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
88        match self {
89            PgAsyncStream::Plain(s) => write_all_plain_async(s, buf).await,
90            #[cfg(feature = "tls")]
91            PgAsyncStream::Tls(s) => s.write_all(buf).await,
92            #[cfg(feature = "tls")]
93            PgAsyncStream::Closed => Err(std::io::Error::new(
94                std::io::ErrorKind::NotConnected,
95                "connection closed",
96            )),
97        }
98    }
99
100    async fn flush(&mut self) -> std::io::Result<()> {
101        match self {
102            PgAsyncStream::Plain(s) => flush_plain_async(s).await,
103            #[cfg(feature = "tls")]
104            PgAsyncStream::Tls(s) => s.flush().await,
105            #[cfg(feature = "tls")]
106            PgAsyncStream::Closed => Err(std::io::Error::new(
107                std::io::ErrorKind::NotConnected,
108                "connection closed",
109            )),
110        }
111    }
112}
113
114#[cfg(feature = "tls")]
115struct AsyncTlsStream {
116    tcp: TcpStream,
117    tls: rustls::ClientConnection,
118}
119
120#[cfg(feature = "tls")]
121impl AsyncTlsStream {
122    async fn handshake(mut tcp: TcpStream, ssl_mode: SslMode, host: &str) -> Result<Self, Error> {
123        let config = tls::build_client_config(ssl_mode)?;
124        let server_name = tls::server_name(host)?;
125        let mut tls = rustls::ClientConnection::new(std::sync::Arc::new(config), server_name)
126            .map_err(|e| connection_error(format!("Failed to create TLS connection: {e}")))?;
127
128        while tls.is_handshaking() {
129            while tls.wants_write() {
130                let mut out = Vec::new();
131                tls.write_tls(&mut out)
132                    .map_err(|e| connection_error(format!("TLS handshake write_tls error: {e}")))?;
133                if !out.is_empty() {
134                    write_all_plain_async(&mut tcp, &out).await.map_err(|e| {
135                        Error::Connection(ConnectionError {
136                            kind: ConnectionErrorKind::Disconnected,
137                            message: format!("TLS handshake write error: {e}"),
138                            source: Some(Box::new(e)),
139                        })
140                    })?;
141                }
142            }
143
144            if tls.wants_read() {
145                let mut buf = [0u8; 8192];
146                let n = read_some_plain_async(&mut tcp, &mut buf)
147                    .await
148                    .map_err(|e| {
149                        Error::Connection(ConnectionError {
150                            kind: ConnectionErrorKind::Disconnected,
151                            message: format!("TLS handshake read error: {e}"),
152                            source: Some(Box::new(e)),
153                        })
154                    })?;
155                if n == 0 {
156                    return Err(connection_error("Connection closed during TLS handshake"));
157                }
158
159                let mut cursor = std::io::Cursor::new(&buf[..n]);
160                tls.read_tls(&mut cursor)
161                    .map_err(|e| connection_error(format!("TLS handshake read_tls error: {e}")))?;
162                tls.process_new_packets()
163                    .map_err(|e| connection_error(format!("TLS handshake error: {e}")))?;
164            }
165        }
166
167        Ok(Self { tcp, tls })
168    }
169
170    async fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
171        let mut read = 0;
172        while read < buf.len() {
173            let n = self.read_plain(&mut buf[read..]).await?;
174            if n == 0 {
175                return Err(std::io::Error::new(
176                    std::io::ErrorKind::UnexpectedEof,
177                    "connection closed",
178                ));
179            }
180            read += n;
181        }
182        Ok(())
183    }
184
185    async fn read_plain(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
186        loop {
187            match self.tls.reader().read(out) {
188                Ok(n) if n > 0 => return Ok(n),
189                Ok(_) => {}
190                Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
191                Err(e) => return Err(e),
192            }
193
194            if !self.tls.wants_read() {
195                return Ok(0);
196            }
197
198            let mut enc = [0u8; 8192];
199            let n = read_some_plain_async(&mut self.tcp, &mut enc).await?;
200            if n == 0 {
201                return Ok(0);
202            }
203
204            let mut cursor = std::io::Cursor::new(&enc[..n]);
205            self.tls.read_tls(&mut cursor)?;
206            self.tls
207                .process_new_packets()
208                .map_err(|e| std::io::Error::other(format!("TLS error: {e}")))?;
209        }
210    }
211
212    async fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
213        let mut written = 0;
214        while written < buf.len() {
215            let n = self.tls.writer().write(&buf[written..])?;
216            if n == 0 {
217                return Err(std::io::Error::new(
218                    std::io::ErrorKind::WriteZero,
219                    "TLS write zero",
220                ));
221            }
222            written += n;
223            self.flush().await?;
224        }
225        Ok(())
226    }
227
228    async fn flush(&mut self) -> std::io::Result<()> {
229        self.tls.writer().flush()?;
230        while self.tls.wants_write() {
231            let mut out = Vec::new();
232            self.tls.write_tls(&mut out)?;
233            if !out.is_empty() {
234                write_all_plain_async(&mut self.tcp, &out).await?;
235            }
236        }
237        flush_plain_async(&mut self.tcp).await
238    }
239}
240
241#[cfg(feature = "tls")]
242async fn read_exact_plain_async(stream: &mut TcpStream, buf: &mut [u8]) -> std::io::Result<()> {
243    let mut read = 0;
244    while read < buf.len() {
245        let n = read_some_plain_async(stream, &mut buf[read..]).await?;
246        if n == 0 {
247            return Err(std::io::Error::new(
248                std::io::ErrorKind::UnexpectedEof,
249                "connection closed",
250            ));
251        }
252        read += n;
253    }
254    Ok(())
255}
256
257async fn read_some_plain_async(stream: &mut TcpStream, buf: &mut [u8]) -> std::io::Result<usize> {
258    let mut read_buf = ReadBuf::new(buf);
259    std::future::poll_fn(|cx| std::pin::Pin::new(&mut *stream).poll_read(cx, &mut read_buf))
260        .await?;
261    Ok(read_buf.filled().len())
262}
263
264async fn write_all_plain_async(stream: &mut TcpStream, buf: &[u8]) -> std::io::Result<()> {
265    let mut written = 0;
266    while written < buf.len() {
267        let n = std::future::poll_fn(|cx| {
268            std::pin::Pin::new(&mut *stream).poll_write(cx, &buf[written..])
269        })
270        .await?;
271        if n == 0 {
272            return Err(std::io::Error::new(
273                std::io::ErrorKind::WriteZero,
274                "connection closed",
275            ));
276        }
277        written += n;
278    }
279    Ok(())
280}
281
282async fn flush_plain_async(stream: &mut TcpStream) -> std::io::Result<()> {
283    std::future::poll_fn(|cx| std::pin::Pin::new(&mut *stream).poll_flush(cx)).await
284}
285
286/// Async PostgreSQL connection.
287///
288/// This connection uses asupersync's TCP stream for non-blocking I/O and
289/// supports the extended query protocol for parameter binding.
290pub struct PgAsyncConnection {
291    stream: PgAsyncStream,
292    state: ConnectionState,
293    process_id: i32,
294    secret_key: i32,
295    parameters: HashMap<String, String>,
296    next_prepared_id: u64,
297    prepared: HashMap<u64, PgPreparedMeta>,
298    config: PgConfig,
299    reader: MessageReader,
300    writer: MessageWriter,
301    read_buf: Vec<u8>,
302}
303
304#[derive(Debug, Clone)]
305struct PgPreparedMeta {
306    name: String,
307    param_type_oids: Vec<u32>,
308}
309
310impl std::fmt::Debug for PgAsyncConnection {
311    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312        f.debug_struct("PgAsyncConnection")
313            .field("state", &self.state)
314            .field("process_id", &self.process_id)
315            .field("host", &self.config.host)
316            .field("port", &self.config.port)
317            .field("database", &self.config.database)
318            .finish_non_exhaustive()
319    }
320}
321
322impl PgAsyncConnection {
323    /// Establish a new async connection to the PostgreSQL server.
324    pub async fn connect(_cx: &Cx, config: PgConfig) -> Outcome<Self, Error> {
325        let addr = config.socket_addr();
326        let socket_addr: std::net::SocketAddr = match addr.parse() {
327            Ok(a) => a,
328            Err(e) => {
329                return Outcome::Err(Error::Connection(ConnectionError {
330                    kind: ConnectionErrorKind::Connect,
331                    message: format!("Invalid socket address: {}", e),
332                    source: None,
333                }));
334            }
335        };
336
337        let stream = match TcpStream::connect_timeout(socket_addr, config.connect_timeout).await {
338            Ok(s) => s,
339            Err(e) => {
340                let kind = if e.kind() == std::io::ErrorKind::ConnectionRefused {
341                    ConnectionErrorKind::Refused
342                } else {
343                    ConnectionErrorKind::Connect
344                };
345                return Outcome::Err(Error::Connection(ConnectionError {
346                    kind,
347                    message: format!("Failed to connect to {}: {}", addr, e),
348                    source: Some(Box::new(e)),
349                }));
350            }
351        };
352
353        stream.set_nodelay(true).ok();
354
355        let mut conn = Self {
356            stream: PgAsyncStream::Plain(stream),
357            state: ConnectionState::Connecting,
358            process_id: 0,
359            secret_key: 0,
360            parameters: HashMap::new(),
361            next_prepared_id: 1,
362            prepared: HashMap::new(),
363            config,
364            reader: MessageReader::new(),
365            writer: MessageWriter::new(),
366            read_buf: vec![0u8; 8192],
367        };
368
369        // SSL negotiation (feature-gated TLS)
370        if conn.config.ssl_mode.should_try_ssl() {
371            #[cfg(feature = "tls")]
372            match conn.negotiate_ssl().await {
373                Outcome::Ok(()) => {}
374                Outcome::Err(e) => return Outcome::Err(e),
375                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
376                Outcome::Panicked(p) => return Outcome::Panicked(p),
377            }
378
379            #[cfg(not(feature = "tls"))]
380            if conn.config.ssl_mode != SslMode::Prefer {
381                return Outcome::Err(connection_error(
382                    "TLS requested but 'sqlmodel-postgres' was built without feature 'tls'",
383                ));
384            }
385        }
386
387        // Startup + authentication
388        if let Outcome::Err(e) = conn.send_startup().await {
389            return Outcome::Err(e);
390        }
391        conn.state = ConnectionState::Authenticating;
392
393        match conn.handle_auth().await {
394            Outcome::Ok(()) => {}
395            Outcome::Err(e) => return Outcome::Err(e),
396            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
397            Outcome::Panicked(p) => return Outcome::Panicked(p),
398        }
399
400        match conn.read_startup_messages().await {
401            Outcome::Ok(()) => Outcome::Ok(conn),
402            Outcome::Err(e) => Outcome::Err(e),
403            Outcome::Cancelled(r) => Outcome::Cancelled(r),
404            Outcome::Panicked(p) => Outcome::Panicked(p),
405        }
406    }
407
408    /// Run a parameterized query and return all rows.
409    pub async fn query_async(
410        &mut self,
411        cx: &Cx,
412        sql: &str,
413        params: &[Value],
414    ) -> Outcome<Vec<Row>, Error> {
415        match self.run_extended(cx, sql, params).await {
416            Outcome::Ok(result) => Outcome::Ok(result.rows),
417            Outcome::Err(e) => Outcome::Err(e),
418            Outcome::Cancelled(r) => Outcome::Cancelled(r),
419            Outcome::Panicked(p) => Outcome::Panicked(p),
420        }
421    }
422
423    /// Execute a statement and return rows affected.
424    pub async fn execute_async(
425        &mut self,
426        cx: &Cx,
427        sql: &str,
428        params: &[Value],
429    ) -> Outcome<u64, Error> {
430        match self.run_extended(cx, sql, params).await {
431            Outcome::Ok(result) => {
432                Outcome::Ok(parse_rows_affected(result.command_tag.as_deref()).unwrap_or(0))
433            }
434            Outcome::Err(e) => Outcome::Err(e),
435            Outcome::Cancelled(r) => Outcome::Cancelled(r),
436            Outcome::Panicked(p) => Outcome::Panicked(p),
437        }
438    }
439
440    /// Execute an INSERT and return the inserted id.
441    ///
442    /// PostgreSQL requires `RETURNING` to retrieve generated IDs. This method
443    /// expects the SQL to return a single-row, single-column result set
444    /// containing an integer id.
445    pub async fn insert_async(
446        &mut self,
447        cx: &Cx,
448        sql: &str,
449        params: &[Value],
450    ) -> Outcome<i64, Error> {
451        let result = match self.run_extended(cx, sql, params).await {
452            Outcome::Ok(r) => r,
453            Outcome::Err(e) => return Outcome::Err(e),
454            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
455            Outcome::Panicked(p) => return Outcome::Panicked(p),
456        };
457
458        let Some(row) = result.rows.first() else {
459            return Outcome::Err(query_error_msg(
460                "INSERT did not return an id; add `RETURNING id`",
461                QueryErrorKind::Database,
462            ));
463        };
464        let Some(id_value) = row.get(0) else {
465            return Outcome::Err(query_error_msg(
466                "INSERT result row missing id column",
467                QueryErrorKind::Database,
468            ));
469        };
470        match id_value.as_i64() {
471            Some(v) => Outcome::Ok(v),
472            None => Outcome::Err(query_error_msg(
473                "INSERT returned non-integer id",
474                QueryErrorKind::Database,
475            )),
476        }
477    }
478
479    /// Ping the server.
480    pub async fn ping_async(&mut self, cx: &Cx) -> Outcome<(), Error> {
481        self.execute_async(cx, "SELECT 1", &[]).await.map(|_| ())
482    }
483
484    /// Close the connection.
485    pub async fn close_async(&mut self, cx: &Cx) -> Outcome<(), Error> {
486        // Best-effort terminate. If this fails, the drop will close the socket.
487        //
488        // Note: server-side prepared statements are released when the connection terminates;
489        // explicit Close/DEALLOCATE is not required for correctness here.
490        let _ = self.send_message(cx, &FrontendMessage::Terminate).await;
491        self.state = ConnectionState::Closed;
492        Outcome::Ok(())
493    }
494
495    // ==================== Prepared statements ====================
496
497    /// Prepare a server-side statement and return a reusable handle.
498    pub async fn prepare_async(&mut self, cx: &Cx, sql: &str) -> Outcome<PreparedStatement, Error> {
499        let stmt_id = self.next_prepared_id;
500        self.next_prepared_id = self.next_prepared_id.saturating_add(1);
501        let stmt_name = format!("sqlmodel_stmt_{stmt_id}");
502
503        if let Outcome::Err(e) = self
504            .send_message(
505                cx,
506                &FrontendMessage::Parse {
507                    name: stmt_name.clone(),
508                    query: sql.to_string(),
509                    // Let PostgreSQL infer types where possible; ambiguous queries will error
510                    // and should add explicit casts.
511                    param_types: Vec::new(),
512                },
513            )
514            .await
515        {
516            return Outcome::Err(e);
517        }
518
519        if let Outcome::Err(e) = self
520            .send_message(
521                cx,
522                &FrontendMessage::Describe {
523                    kind: DescribeKind::Statement,
524                    name: stmt_name.clone(),
525                },
526            )
527            .await
528        {
529            return Outcome::Err(e);
530        }
531
532        if let Outcome::Err(e) = self.send_message(cx, &FrontendMessage::Sync).await {
533            return Outcome::Err(e);
534        }
535
536        let mut param_type_oids: Option<Vec<u32>> = None;
537        let mut columns: Option<Vec<String>> = None;
538
539        loop {
540            let msg = match self.receive_message(cx).await {
541                Outcome::Ok(m) => m,
542                Outcome::Err(e) => return Outcome::Err(e),
543                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
544                Outcome::Panicked(p) => return Outcome::Panicked(p),
545            };
546
547            match msg {
548                BackendMessage::ParseComplete
549                | BackendMessage::BindComplete
550                | BackendMessage::CloseComplete
551                | BackendMessage::NoData
552                | BackendMessage::EmptyQueryResponse => {}
553                BackendMessage::ParameterDescription(oids) => {
554                    param_type_oids = Some(oids);
555                }
556                BackendMessage::RowDescription(desc) => {
557                    columns = Some(desc.iter().map(|f| f.name.clone()).collect());
558                }
559                BackendMessage::ReadyForQuery(status) => {
560                    self.state = ConnectionState::Ready(TransactionStatusState::from(status));
561                    break;
562                }
563                BackendMessage::ErrorResponse(e) => {
564                    self.state = ConnectionState::Error;
565                    return Outcome::Err(error_from_fields(&e));
566                }
567                BackendMessage::NoticeResponse(_notice) => {}
568                other => {
569                    return Outcome::Err(protocol_error(format!(
570                        "Unexpected message during prepare: {other:?}"
571                    )));
572                }
573            }
574        }
575
576        let param_type_oids = param_type_oids.unwrap_or_default();
577        self.prepared.insert(
578            stmt_id,
579            PgPreparedMeta {
580                name: stmt_name,
581                param_type_oids: param_type_oids.clone(),
582            },
583        );
584
585        match columns {
586            Some(cols) => Outcome::Ok(PreparedStatement::with_columns(
587                stmt_id,
588                sql.to_string(),
589                param_type_oids.len(),
590                cols,
591            )),
592            None => Outcome::Ok(PreparedStatement::new(
593                stmt_id,
594                sql.to_string(),
595                param_type_oids.len(),
596            )),
597        }
598    }
599
600    pub async fn query_prepared_async(
601        &mut self,
602        cx: &Cx,
603        stmt: &PreparedStatement,
604        params: &[Value],
605    ) -> Outcome<Vec<Row>, Error> {
606        let meta = match self.prepared.get(&stmt.id()) {
607            Some(m) => m.clone(),
608            None => {
609                return Outcome::Err(query_error_msg(
610                    format!("Unknown prepared statement id {}", stmt.id()),
611                    QueryErrorKind::Database,
612                ));
613            }
614        };
615
616        if meta.param_type_oids.len() != params.len() {
617            return Outcome::Err(query_error_msg(
618                format!(
619                    "Prepared statement expects {} params, got {}",
620                    meta.param_type_oids.len(),
621                    params.len()
622                ),
623                QueryErrorKind::Database,
624            ));
625        }
626
627        match self.run_prepared(cx, &meta, params).await {
628            Outcome::Ok(result) => Outcome::Ok(result.rows),
629            Outcome::Err(e) => Outcome::Err(e),
630            Outcome::Cancelled(r) => Outcome::Cancelled(r),
631            Outcome::Panicked(p) => Outcome::Panicked(p),
632        }
633    }
634
635    pub async fn execute_prepared_async(
636        &mut self,
637        cx: &Cx,
638        stmt: &PreparedStatement,
639        params: &[Value],
640    ) -> Outcome<u64, Error> {
641        let meta = match self.prepared.get(&stmt.id()) {
642            Some(m) => m.clone(),
643            None => {
644                return Outcome::Err(query_error_msg(
645                    format!("Unknown prepared statement id {}", stmt.id()),
646                    QueryErrorKind::Database,
647                ));
648            }
649        };
650
651        if meta.param_type_oids.len() != params.len() {
652            return Outcome::Err(query_error_msg(
653                format!(
654                    "Prepared statement expects {} params, got {}",
655                    meta.param_type_oids.len(),
656                    params.len()
657                ),
658                QueryErrorKind::Database,
659            ));
660        }
661
662        match self.run_prepared(cx, &meta, params).await {
663            Outcome::Ok(result) => {
664                Outcome::Ok(parse_rows_affected(result.command_tag.as_deref()).unwrap_or(0))
665            }
666            Outcome::Err(e) => Outcome::Err(e),
667            Outcome::Cancelled(r) => Outcome::Cancelled(r),
668            Outcome::Panicked(p) => Outcome::Panicked(p),
669        }
670    }
671
672    // ==================== Protocol: extended query ====================
673
674    async fn read_extended_result(&mut self, cx: &Cx) -> Outcome<PgQueryResult, Error> {
675        // Read responses until ReadyForQuery
676        let mut field_descs: Option<Vec<crate::protocol::FieldDescription>> = None;
677        let mut columns: Option<Arc<ColumnInfo>> = None;
678        let mut rows: Vec<Row> = Vec::new();
679        let mut command_tag: Option<String> = None;
680
681        loop {
682            let msg = match self.receive_message(cx).await {
683                Outcome::Ok(m) => m,
684                Outcome::Err(e) => return Outcome::Err(e),
685                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
686                Outcome::Panicked(p) => return Outcome::Panicked(p),
687            };
688
689            match msg {
690                BackendMessage::ParseComplete
691                | BackendMessage::BindComplete
692                | BackendMessage::CloseComplete
693                | BackendMessage::ParameterDescription(_)
694                | BackendMessage::NoData
695                | BackendMessage::PortalSuspended
696                | BackendMessage::EmptyQueryResponse => {}
697                BackendMessage::RowDescription(desc) => {
698                    let names: Vec<String> = desc.iter().map(|f| f.name.clone()).collect();
699                    columns = Some(Arc::new(ColumnInfo::new(names)));
700                    field_descs = Some(desc);
701                }
702                BackendMessage::DataRow(raw_values) => {
703                    let Some(ref desc) = field_descs else {
704                        return Outcome::Err(protocol_error(
705                            "DataRow received before RowDescription",
706                        ));
707                    };
708                    let Some(ref cols) = columns else {
709                        return Outcome::Err(protocol_error("Row column metadata missing"));
710                    };
711                    if raw_values.len() != desc.len() {
712                        return Outcome::Err(protocol_error("DataRow field count mismatch"));
713                    }
714
715                    let mut values = Vec::with_capacity(raw_values.len());
716                    for (i, raw) in raw_values.into_iter().enumerate() {
717                        match raw {
718                            None => values.push(Value::Null),
719                            Some(bytes) => {
720                                let field = &desc[i];
721                                let format = Format::from_code(field.format);
722                                let decoded = match decode_value(
723                                    field.type_oid,
724                                    Some(bytes.as_slice()),
725                                    format,
726                                ) {
727                                    Ok(v) => v,
728                                    Err(e) => return Outcome::Err(e),
729                                };
730                                values.push(decoded);
731                            }
732                        }
733                    }
734                    rows.push(Row::with_columns(Arc::clone(cols), values));
735                }
736                BackendMessage::CommandComplete(tag) => {
737                    command_tag = Some(tag);
738                }
739                BackendMessage::ReadyForQuery(status) => {
740                    self.state = ConnectionState::Ready(TransactionStatusState::from(status));
741                    break;
742                }
743                BackendMessage::ErrorResponse(e) => {
744                    self.state = ConnectionState::Error;
745                    return Outcome::Err(error_from_fields(&e));
746                }
747                BackendMessage::NoticeResponse(_notice) => {}
748                _ => {}
749            }
750        }
751
752        Outcome::Ok(PgQueryResult { rows, command_tag })
753    }
754
755    async fn run_extended(
756        &mut self,
757        cx: &Cx,
758        sql: &str,
759        params: &[Value],
760    ) -> Outcome<PgQueryResult, Error> {
761        // Encode parameters
762        let mut param_types = Vec::with_capacity(params.len());
763        let mut param_values = Vec::with_capacity(params.len());
764
765        for v in params {
766            if matches!(v, Value::Null) {
767                param_types.push(0);
768                param_values.push(None);
769                continue;
770            }
771            match encode_value(v, Format::Text) {
772                Ok((bytes, oid)) => {
773                    param_types.push(oid);
774                    param_values.push(Some(bytes));
775                }
776                Err(e) => return Outcome::Err(e),
777            }
778        }
779
780        // Parse + bind unnamed statement/portal
781        if let Outcome::Err(e) = self
782            .send_message(
783                cx,
784                &FrontendMessage::Parse {
785                    name: String::new(),
786                    query: sql.to_string(),
787                    param_types,
788                },
789            )
790            .await
791        {
792            return Outcome::Err(e);
793        }
794
795        let param_formats = if params.is_empty() {
796            Vec::new()
797        } else {
798            vec![Format::Text.code()]
799        };
800        if let Outcome::Err(e) = self
801            .send_message(
802                cx,
803                &FrontendMessage::Bind {
804                    portal: String::new(),
805                    statement: String::new(),
806                    param_formats,
807                    params: param_values,
808                    // Default result formats (text) when empty.
809                    result_formats: Vec::new(),
810                },
811            )
812            .await
813        {
814            return Outcome::Err(e);
815        }
816
817        if let Outcome::Err(e) = self
818            .send_message(
819                cx,
820                &FrontendMessage::Describe {
821                    kind: DescribeKind::Portal,
822                    name: String::new(),
823                },
824            )
825            .await
826        {
827            return Outcome::Err(e);
828        }
829
830        if let Outcome::Err(e) = self
831            .send_message(
832                cx,
833                &FrontendMessage::Execute {
834                    portal: String::new(),
835                    max_rows: 0,
836                },
837            )
838            .await
839        {
840            return Outcome::Err(e);
841        }
842
843        if let Outcome::Err(e) = self.send_message(cx, &FrontendMessage::Sync).await {
844            return Outcome::Err(e);
845        }
846        self.read_extended_result(cx).await
847    }
848
849    async fn run_prepared(
850        &mut self,
851        cx: &Cx,
852        meta: &PgPreparedMeta,
853        params: &[Value],
854    ) -> Outcome<PgQueryResult, Error> {
855        let mut param_values = Vec::with_capacity(params.len());
856
857        for (i, v) in params.iter().enumerate() {
858            if matches!(v, Value::Null) {
859                param_values.push(None);
860                continue;
861            }
862            match encode_value(v, Format::Text) {
863                Ok((bytes, oid)) => {
864                    let expected = meta.param_type_oids.get(i).copied().unwrap_or(0);
865                    if expected != 0 && expected != oid {
866                        return Outcome::Err(query_error_msg(
867                            format!(
868                                "Prepared statement param {} expects type OID {}, got {}",
869                                i + 1,
870                                expected,
871                                oid
872                            ),
873                            QueryErrorKind::Database,
874                        ));
875                    }
876                    param_values.push(Some(bytes));
877                }
878                Err(e) => return Outcome::Err(e),
879            }
880        }
881
882        let param_formats = if params.is_empty() {
883            Vec::new()
884        } else {
885            vec![Format::Text.code()]
886        };
887
888        if let Outcome::Err(e) = self
889            .send_message(
890                cx,
891                &FrontendMessage::Bind {
892                    portal: String::new(),
893                    statement: meta.name.clone(),
894                    param_formats,
895                    params: param_values,
896                    result_formats: Vec::new(),
897                },
898            )
899            .await
900        {
901            return Outcome::Err(e);
902        }
903
904        if let Outcome::Err(e) = self
905            .send_message(
906                cx,
907                &FrontendMessage::Describe {
908                    kind: DescribeKind::Portal,
909                    name: String::new(),
910                },
911            )
912            .await
913        {
914            return Outcome::Err(e);
915        }
916
917        if let Outcome::Err(e) = self
918            .send_message(
919                cx,
920                &FrontendMessage::Execute {
921                    portal: String::new(),
922                    max_rows: 0,
923                },
924            )
925            .await
926        {
927            return Outcome::Err(e);
928        }
929
930        if let Outcome::Err(e) = self.send_message(cx, &FrontendMessage::Sync).await {
931            return Outcome::Err(e);
932        }
933
934        self.read_extended_result(cx).await
935    }
936
937    // ==================== Startup + auth ====================
938
939    #[cfg(feature = "tls")]
940    async fn negotiate_ssl(&mut self) -> Outcome<(), Error> {
941        // Send SSL request
942        if let Outcome::Err(e) = self.send_message_no_cx(&FrontendMessage::SSLRequest).await {
943            return Outcome::Err(e);
944        }
945
946        // Read single-byte response
947        let mut buf = [0u8; 1];
948        if let Err(e) = self.stream.read_exact(&mut buf).await {
949            return Outcome::Err(Error::Connection(ConnectionError {
950                kind: ConnectionErrorKind::Ssl,
951                message: format!("Failed to read SSL response: {}", e),
952                source: Some(Box::new(e)),
953            }));
954        }
955
956        match buf[0] {
957            b'S' => {
958                #[cfg(feature = "tls")]
959                {
960                    let plain = match std::mem::replace(&mut self.stream, PgAsyncStream::Closed) {
961                        PgAsyncStream::Plain(s) => s,
962                        other => {
963                            self.stream = other;
964                            return Outcome::Err(connection_error(
965                                "TLS upgrade requires a plain TCP stream",
966                            ));
967                        }
968                    };
969
970                    let tls_stream = match AsyncTlsStream::handshake(
971                        plain,
972                        self.config.ssl_mode,
973                        &self.config.host,
974                    )
975                    .await
976                    {
977                        Ok(s) => s,
978                        Err(e) => return Outcome::Err(e),
979                    };
980
981                    self.stream = PgAsyncStream::Tls(tls_stream);
982                    Outcome::Ok(())
983                }
984
985                #[cfg(not(feature = "tls"))]
986                {
987                    Outcome::Err(connection_error(
988                        "TLS requested but 'sqlmodel-postgres' was built without feature 'tls'",
989                    ))
990                }
991            }
992            b'N' => {
993                if self.config.ssl_mode.is_required() {
994                    Outcome::Err(Error::Connection(ConnectionError {
995                        kind: ConnectionErrorKind::Ssl,
996                        message: "Server does not support SSL".to_string(),
997                        source: None,
998                    }))
999                } else {
1000                    Outcome::Ok(())
1001                }
1002            }
1003            other => Outcome::Err(Error::Connection(ConnectionError {
1004                kind: ConnectionErrorKind::Ssl,
1005                message: format!("Unexpected SSL response: 0x{other:02x}"),
1006                source: None,
1007            })),
1008        }
1009    }
1010
1011    async fn send_startup(&mut self) -> Outcome<(), Error> {
1012        let params = self.config.startup_params();
1013        self.send_message_no_cx(&FrontendMessage::Startup {
1014            version: PROTOCOL_VERSION,
1015            params,
1016        })
1017        .await
1018    }
1019
1020    fn require_auth_value(&self, message: &'static str) -> Outcome<&str, Error> {
1021        // NOTE: Auth values are sourced from runtime config, not hardcoded.
1022        match self.config.password.as_deref() {
1023            Some(password) => Outcome::Ok(password),
1024            None => Outcome::Err(auth_error(message)),
1025        }
1026    }
1027
1028    async fn handle_auth(&mut self) -> Outcome<(), Error> {
1029        loop {
1030            let msg = match self.receive_message_no_cx().await {
1031                Outcome::Ok(m) => m,
1032                Outcome::Err(e) => return Outcome::Err(e),
1033                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1034                Outcome::Panicked(p) => return Outcome::Panicked(p),
1035            };
1036
1037            match msg {
1038                BackendMessage::AuthenticationOk => return Outcome::Ok(()),
1039                BackendMessage::AuthenticationCleartextPassword => {
1040                    let auth_value = match self
1041                        .require_auth_value("Authentication value required but not provided")
1042                    {
1043                        Outcome::Ok(password) => password,
1044                        Outcome::Err(e) => return Outcome::Err(e),
1045                        Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1046                        Outcome::Panicked(p) => return Outcome::Panicked(p),
1047                    };
1048                    if let Outcome::Err(e) = self
1049                        .send_message_no_cx(&FrontendMessage::PasswordMessage(
1050                            auth_value.to_string(),
1051                        ))
1052                        .await
1053                    {
1054                        return Outcome::Err(e);
1055                    }
1056                }
1057                BackendMessage::AuthenticationMD5Password(salt) => {
1058                    let auth_value = match self
1059                        .require_auth_value("Authentication value required but not provided")
1060                    {
1061                        Outcome::Ok(password) => password,
1062                        Outcome::Err(e) => return Outcome::Err(e),
1063                        Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1064                        Outcome::Panicked(p) => return Outcome::Panicked(p),
1065                    };
1066                    let hash = md5_password(&self.config.user, auth_value, salt);
1067                    if let Outcome::Err(e) = self
1068                        .send_message_no_cx(&FrontendMessage::PasswordMessage(hash))
1069                        .await
1070                    {
1071                        return Outcome::Err(e);
1072                    }
1073                }
1074                BackendMessage::AuthenticationSASL(mechanisms) => {
1075                    if mechanisms.contains(&"SCRAM-SHA-256".to_string()) {
1076                        match self.scram_auth().await {
1077                            Outcome::Ok(()) => {}
1078                            Outcome::Err(e) => return Outcome::Err(e),
1079                            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1080                            Outcome::Panicked(p) => return Outcome::Panicked(p),
1081                        }
1082                    } else {
1083                        return Outcome::Err(auth_error(format!(
1084                            "Unsupported SASL mechanisms: {:?}",
1085                            mechanisms
1086                        )));
1087                    }
1088                }
1089                BackendMessage::ErrorResponse(e) => {
1090                    self.state = ConnectionState::Error;
1091                    return Outcome::Err(error_from_fields(&e));
1092                }
1093                other => {
1094                    return Outcome::Err(protocol_error(format!(
1095                        "Unexpected message during auth: {other:?}"
1096                    )));
1097                }
1098            }
1099        }
1100    }
1101
1102    async fn scram_auth(&mut self) -> Outcome<(), Error> {
1103        let auth_value =
1104            match self.require_auth_value("Authentication value required for SCRAM-SHA-256") {
1105                Outcome::Ok(password) => password,
1106                Outcome::Err(e) => return Outcome::Err(e),
1107                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1108                Outcome::Panicked(p) => return Outcome::Panicked(p),
1109            };
1110
1111        let mut client = ScramClient::new(&self.config.user, auth_value);
1112
1113        // Client-first
1114        let client_first = client.client_first();
1115        if let Outcome::Err(e) = self
1116            .send_message_no_cx(&FrontendMessage::SASLInitialResponse {
1117                mechanism: "SCRAM-SHA-256".to_string(),
1118                data: client_first,
1119            })
1120            .await
1121        {
1122            return Outcome::Err(e);
1123        }
1124
1125        // Server-first
1126        let msg = match self.receive_message_no_cx().await {
1127            Outcome::Ok(m) => m,
1128            Outcome::Err(e) => return Outcome::Err(e),
1129            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1130            Outcome::Panicked(p) => return Outcome::Panicked(p),
1131        };
1132        let server_first_data = match msg {
1133            BackendMessage::AuthenticationSASLContinue(data) => data,
1134            BackendMessage::ErrorResponse(e) => {
1135                self.state = ConnectionState::Error;
1136                return Outcome::Err(error_from_fields(&e));
1137            }
1138            other => {
1139                return Outcome::Err(protocol_error(format!(
1140                    "Expected SASL continue, got: {other:?}"
1141                )));
1142            }
1143        };
1144
1145        // Client-final
1146        let client_final = match client.process_server_first(&server_first_data) {
1147            Ok(v) => v,
1148            Err(e) => return Outcome::Err(e),
1149        };
1150        if let Outcome::Err(e) = self
1151            .send_message_no_cx(&FrontendMessage::SASLResponse(client_final))
1152            .await
1153        {
1154            return Outcome::Err(e);
1155        }
1156
1157        // Server-final
1158        let msg = match self.receive_message_no_cx().await {
1159            Outcome::Ok(m) => m,
1160            Outcome::Err(e) => return Outcome::Err(e),
1161            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1162            Outcome::Panicked(p) => return Outcome::Panicked(p),
1163        };
1164        let server_final_data = match msg {
1165            BackendMessage::AuthenticationSASLFinal(data) => data,
1166            BackendMessage::ErrorResponse(e) => {
1167                self.state = ConnectionState::Error;
1168                return Outcome::Err(error_from_fields(&e));
1169            }
1170            other => {
1171                return Outcome::Err(protocol_error(format!(
1172                    "Expected SASL final, got: {other:?}"
1173                )));
1174            }
1175        };
1176
1177        if let Err(e) = client.verify_server_final(&server_final_data) {
1178            return Outcome::Err(e);
1179        }
1180
1181        // Wait for AuthenticationOk
1182        let msg = match self.receive_message_no_cx().await {
1183            Outcome::Ok(m) => m,
1184            Outcome::Err(e) => return Outcome::Err(e),
1185            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1186            Outcome::Panicked(p) => return Outcome::Panicked(p),
1187        };
1188        match msg {
1189            BackendMessage::AuthenticationOk => Outcome::Ok(()),
1190            BackendMessage::ErrorResponse(e) => {
1191                self.state = ConnectionState::Error;
1192                Outcome::Err(error_from_fields(&e))
1193            }
1194            other => Outcome::Err(protocol_error(format!(
1195                "Expected AuthenticationOk, got: {other:?}"
1196            ))),
1197        }
1198    }
1199
1200    async fn read_startup_messages(&mut self) -> Outcome<(), Error> {
1201        loop {
1202            let msg = match self.receive_message_no_cx().await {
1203                Outcome::Ok(m) => m,
1204                Outcome::Err(e) => return Outcome::Err(e),
1205                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1206                Outcome::Panicked(p) => return Outcome::Panicked(p),
1207            };
1208
1209            match msg {
1210                BackendMessage::BackendKeyData {
1211                    process_id,
1212                    secret_key,
1213                } => {
1214                    self.process_id = process_id;
1215                    self.secret_key = secret_key;
1216                }
1217                BackendMessage::ParameterStatus { name, value } => {
1218                    self.parameters.insert(name, value);
1219                }
1220                BackendMessage::ReadyForQuery(status) => {
1221                    self.state = ConnectionState::Ready(TransactionStatusState::from(status));
1222                    return Outcome::Ok(());
1223                }
1224                BackendMessage::ErrorResponse(e) => {
1225                    self.state = ConnectionState::Error;
1226                    return Outcome::Err(error_from_fields(&e));
1227                }
1228                BackendMessage::NoticeResponse(_notice) => {}
1229                other => {
1230                    return Outcome::Err(protocol_error(format!(
1231                        "Unexpected startup message: {other:?}"
1232                    )));
1233                }
1234            }
1235        }
1236    }
1237
1238    // ==================== I/O ====================
1239
1240    async fn send_message(&mut self, cx: &Cx, msg: &FrontendMessage) -> Outcome<(), Error> {
1241        // If cancelled, propagate early.
1242        if let Some(reason) = cx.cancel_reason() {
1243            return Outcome::Cancelled(reason);
1244        }
1245        self.send_message_no_cx(msg).await
1246    }
1247
1248    async fn receive_message(&mut self, cx: &Cx) -> Outcome<BackendMessage, Error> {
1249        if let Some(reason) = cx.cancel_reason() {
1250            return Outcome::Cancelled(reason);
1251        }
1252        self.receive_message_no_cx().await
1253    }
1254
1255    async fn send_message_no_cx(&mut self, msg: &FrontendMessage) -> Outcome<(), Error> {
1256        let data = self.writer.write(msg).to_vec();
1257
1258        if let Err(e) = self.stream.write_all(&data).await {
1259            self.state = ConnectionState::Error;
1260            return Outcome::Err(Error::Connection(ConnectionError {
1261                kind: ConnectionErrorKind::Disconnected,
1262                message: format!("Failed to write to server: {}", e),
1263                source: Some(Box::new(e)),
1264            }));
1265        }
1266
1267        if let Err(e) = self.stream.flush().await {
1268            self.state = ConnectionState::Error;
1269            return Outcome::Err(Error::Connection(ConnectionError {
1270                kind: ConnectionErrorKind::Disconnected,
1271                message: format!("Failed to flush stream: {}", e),
1272                source: Some(Box::new(e)),
1273            }));
1274        }
1275
1276        Outcome::Ok(())
1277    }
1278
1279    async fn receive_message_no_cx(&mut self) -> Outcome<BackendMessage, Error> {
1280        loop {
1281            match self.reader.next_message() {
1282                Ok(Some(msg)) => return Outcome::Ok(msg),
1283                Ok(None) => {}
1284                Err(e) => {
1285                    self.state = ConnectionState::Error;
1286                    return Outcome::Err(protocol_error(format!("Protocol error: {}", e)));
1287                }
1288            }
1289
1290            let n = match self.stream.read_some(&mut self.read_buf).await {
1291                Ok(n) => n,
1292                Err(e) => {
1293                    self.state = ConnectionState::Error;
1294                    return Outcome::Err(match e.kind() {
1295                        std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock => {
1296                            Error::Timeout
1297                        }
1298                        _ => Error::Connection(ConnectionError {
1299                            kind: ConnectionErrorKind::Disconnected,
1300                            message: format!("Failed to read from server: {}", e),
1301                            source: Some(Box::new(e)),
1302                        }),
1303                    });
1304                }
1305            };
1306
1307            if n == 0 {
1308                self.state = ConnectionState::Disconnected;
1309                return Outcome::Err(Error::Connection(ConnectionError {
1310                    kind: ConnectionErrorKind::Disconnected,
1311                    message: "Connection closed by server".to_string(),
1312                    source: None,
1313                }));
1314            }
1315
1316            // Only append raw bytes; let next_message() at the top of the
1317            // loop handle parsing.  The old code called feed() here, which
1318            // parsed *and consumed* all complete messages from the buffer and
1319            // returned them in a Vec — but the caller only checked for Err,
1320            // silently discarding the Ok(messages).  On the next iteration
1321            // next_message() would see an empty buffer and block forever on
1322            // the socket read.  (See issue #9.)
1323            self.reader.push(&self.read_buf[..n]);
1324        }
1325    }
1326}
1327
1328/// Shared, cloneable PostgreSQL connection with interior mutability.
1329pub struct SharedPgConnection {
1330    inner: Arc<Mutex<PgAsyncConnection>>,
1331}
1332
1333impl SharedPgConnection {
1334    pub fn new(conn: PgAsyncConnection) -> Self {
1335        Self {
1336            inner: Arc::new(Mutex::new(conn)),
1337        }
1338    }
1339
1340    pub async fn connect(cx: &Cx, config: PgConfig) -> Outcome<Self, Error> {
1341        match PgAsyncConnection::connect(cx, config).await {
1342            Outcome::Ok(conn) => Outcome::Ok(Self::new(conn)),
1343            Outcome::Err(e) => Outcome::Err(e),
1344            Outcome::Cancelled(r) => Outcome::Cancelled(r),
1345            Outcome::Panicked(p) => Outcome::Panicked(p),
1346        }
1347    }
1348
1349    pub fn inner(&self) -> &Arc<Mutex<PgAsyncConnection>> {
1350        &self.inner
1351    }
1352
1353    async fn begin_transaction_impl(
1354        &self,
1355        cx: &Cx,
1356        isolation: Option<IsolationLevel>,
1357    ) -> Outcome<SharedPgTransaction<'_>, Error> {
1358        let inner = Arc::clone(&self.inner);
1359        let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1360            return Outcome::Err(connection_error("Failed to acquire connection lock"));
1361        };
1362
1363        if let Some(level) = isolation {
1364            let sql = format!("SET TRANSACTION ISOLATION LEVEL {}", level.as_sql());
1365            match guard.execute_async(cx, &sql, &[]).await {
1366                Outcome::Ok(_) => {}
1367                Outcome::Err(e) => return Outcome::Err(e),
1368                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1369                Outcome::Panicked(p) => return Outcome::Panicked(p),
1370            }
1371        }
1372
1373        match guard.execute_async(cx, "BEGIN", &[]).await {
1374            Outcome::Ok(_) => {}
1375            Outcome::Err(e) => return Outcome::Err(e),
1376            Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1377            Outcome::Panicked(p) => return Outcome::Panicked(p),
1378        }
1379
1380        drop(guard);
1381        Outcome::Ok(SharedPgTransaction {
1382            inner,
1383            committed: false,
1384            _marker: std::marker::PhantomData,
1385        })
1386    }
1387}
1388
1389impl Clone for SharedPgConnection {
1390    fn clone(&self) -> Self {
1391        Self {
1392            inner: Arc::clone(&self.inner),
1393        }
1394    }
1395}
1396
1397impl std::fmt::Debug for SharedPgConnection {
1398    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1399        f.debug_struct("SharedPgConnection")
1400            .field("inner", &"Arc<Mutex<PgAsyncConnection>>")
1401            .finish()
1402    }
1403}
1404
1405pub struct SharedPgTransaction<'conn> {
1406    inner: Arc<Mutex<PgAsyncConnection>>,
1407    committed: bool,
1408    _marker: std::marker::PhantomData<&'conn ()>,
1409}
1410
1411impl<'conn> Drop for SharedPgTransaction<'conn> {
1412    fn drop(&mut self) {
1413        if !self.committed {
1414            // WARNING: Transaction was dropped without commit() or rollback()!
1415            // We cannot do async work in Drop, so the PostgreSQL transaction will
1416            // remain open until the connection is closed or a new transaction
1417            // is started.
1418            #[cfg(debug_assertions)]
1419            eprintln!(
1420                "WARNING: SharedPgTransaction dropped without commit/rollback. \
1421                 The PostgreSQL transaction may still be open."
1422            );
1423        }
1424    }
1425}
1426
1427impl Connection for SharedPgConnection {
1428    type Tx<'conn>
1429        = SharedPgTransaction<'conn>
1430    where
1431        Self: 'conn;
1432
1433    fn dialect(&self) -> sqlmodel_core::Dialect {
1434        sqlmodel_core::Dialect::Postgres
1435    }
1436
1437    fn query(
1438        &self,
1439        cx: &Cx,
1440        sql: &str,
1441        params: &[Value],
1442    ) -> impl Future<Output = Outcome<Vec<Row>, Error>> + Send {
1443        let inner = Arc::clone(&self.inner);
1444        let sql = sql.to_string();
1445        let params = params.to_vec();
1446        async move {
1447            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1448                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1449            };
1450            guard.query_async(cx, &sql, &params).await
1451        }
1452    }
1453
1454    fn query_one(
1455        &self,
1456        cx: &Cx,
1457        sql: &str,
1458        params: &[Value],
1459    ) -> impl Future<Output = Outcome<Option<Row>, Error>> + Send {
1460        let inner = Arc::clone(&self.inner);
1461        let sql = sql.to_string();
1462        let params = params.to_vec();
1463        async move {
1464            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1465                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1466            };
1467            let rows = match guard.query_async(cx, &sql, &params).await {
1468                Outcome::Ok(r) => r,
1469                Outcome::Err(e) => return Outcome::Err(e),
1470                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1471                Outcome::Panicked(p) => return Outcome::Panicked(p),
1472            };
1473            Outcome::Ok(rows.into_iter().next())
1474        }
1475    }
1476
1477    fn execute(
1478        &self,
1479        cx: &Cx,
1480        sql: &str,
1481        params: &[Value],
1482    ) -> impl Future<Output = Outcome<u64, Error>> + Send {
1483        let inner = Arc::clone(&self.inner);
1484        let sql = sql.to_string();
1485        let params = params.to_vec();
1486        async move {
1487            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1488                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1489            };
1490            guard.execute_async(cx, &sql, &params).await
1491        }
1492    }
1493
1494    fn insert(
1495        &self,
1496        cx: &Cx,
1497        sql: &str,
1498        params: &[Value],
1499    ) -> impl Future<Output = Outcome<i64, Error>> + Send {
1500        let inner = Arc::clone(&self.inner);
1501        let sql = sql.to_string();
1502        let params = params.to_vec();
1503        async move {
1504            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1505                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1506            };
1507            guard.insert_async(cx, &sql, &params).await
1508        }
1509    }
1510
1511    fn batch(
1512        &self,
1513        cx: &Cx,
1514        statements: &[(String, Vec<Value>)],
1515    ) -> impl Future<Output = Outcome<Vec<u64>, Error>> + Send {
1516        let inner = Arc::clone(&self.inner);
1517        let statements = statements.to_vec();
1518        async move {
1519            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1520                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1521            };
1522            let mut results = Vec::with_capacity(statements.len());
1523            for (sql, params) in &statements {
1524                match guard.execute_async(cx, sql, params).await {
1525                    Outcome::Ok(n) => results.push(n),
1526                    Outcome::Err(e) => return Outcome::Err(e),
1527                    Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1528                    Outcome::Panicked(p) => return Outcome::Panicked(p),
1529                }
1530            }
1531            Outcome::Ok(results)
1532        }
1533    }
1534
1535    fn begin(&self, cx: &Cx) -> impl Future<Output = Outcome<Self::Tx<'_>, Error>> + Send {
1536        self.begin_with(cx, IsolationLevel::default())
1537    }
1538
1539    fn begin_with(
1540        &self,
1541        cx: &Cx,
1542        isolation: IsolationLevel,
1543    ) -> impl Future<Output = Outcome<Self::Tx<'_>, Error>> + Send {
1544        self.begin_transaction_impl(cx, Some(isolation))
1545    }
1546
1547    fn prepare(
1548        &self,
1549        cx: &Cx,
1550        sql: &str,
1551    ) -> impl Future<Output = Outcome<PreparedStatement, Error>> + Send {
1552        let inner = Arc::clone(&self.inner);
1553        let sql = sql.to_string();
1554        async move {
1555            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1556                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1557            };
1558            guard.prepare_async(cx, &sql).await
1559        }
1560    }
1561
1562    fn query_prepared(
1563        &self,
1564        cx: &Cx,
1565        stmt: &PreparedStatement,
1566        params: &[Value],
1567    ) -> impl Future<Output = Outcome<Vec<Row>, Error>> + Send {
1568        let inner = Arc::clone(&self.inner);
1569        let stmt = stmt.clone();
1570        let params = params.to_vec();
1571        async move {
1572            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1573                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1574            };
1575            guard.query_prepared_async(cx, &stmt, &params).await
1576        }
1577    }
1578
1579    fn execute_prepared(
1580        &self,
1581        cx: &Cx,
1582        stmt: &PreparedStatement,
1583        params: &[Value],
1584    ) -> impl Future<Output = Outcome<u64, Error>> + Send {
1585        let inner = Arc::clone(&self.inner);
1586        let stmt = stmt.clone();
1587        let params = params.to_vec();
1588        async move {
1589            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1590                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1591            };
1592            guard.execute_prepared_async(cx, &stmt, &params).await
1593        }
1594    }
1595
1596    fn ping(&self, cx: &Cx) -> impl Future<Output = Outcome<(), Error>> + Send {
1597        let inner = Arc::clone(&self.inner);
1598        async move {
1599            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1600                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1601            };
1602            guard.ping_async(cx).await
1603        }
1604    }
1605
1606    async fn close(self, cx: &Cx) -> sqlmodel_core::Result<()> {
1607        let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&self.inner), cx).await else {
1608            return Err(connection_error("Failed to acquire connection lock"));
1609        };
1610        match guard.close_async(cx).await {
1611            Outcome::Ok(()) => Ok(()),
1612            Outcome::Err(e) => Err(e),
1613            Outcome::Cancelled(r) => Err(Error::Query(QueryError {
1614                kind: QueryErrorKind::Cancelled,
1615                message: format!("Cancelled: {r:?}"),
1616                sqlstate: None,
1617                sql: None,
1618                detail: None,
1619                hint: None,
1620                position: None,
1621                source: None,
1622            })),
1623            Outcome::Panicked(p) => Err(Error::Protocol(ProtocolError {
1624                message: format!("Panicked: {p:?}"),
1625                raw_data: None,
1626                source: None,
1627            })),
1628        }
1629    }
1630}
1631
1632impl<'conn> TransactionOps for SharedPgTransaction<'conn> {
1633    fn query(
1634        &self,
1635        cx: &Cx,
1636        sql: &str,
1637        params: &[Value],
1638    ) -> impl Future<Output = Outcome<Vec<Row>, Error>> + Send {
1639        let inner = Arc::clone(&self.inner);
1640        let sql = sql.to_string();
1641        let params = params.to_vec();
1642        async move {
1643            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1644                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1645            };
1646            guard.query_async(cx, &sql, &params).await
1647        }
1648    }
1649
1650    fn query_one(
1651        &self,
1652        cx: &Cx,
1653        sql: &str,
1654        params: &[Value],
1655    ) -> impl Future<Output = Outcome<Option<Row>, Error>> + Send {
1656        let inner = Arc::clone(&self.inner);
1657        let sql = sql.to_string();
1658        let params = params.to_vec();
1659        async move {
1660            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1661                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1662            };
1663            let rows = match guard.query_async(cx, &sql, &params).await {
1664                Outcome::Ok(r) => r,
1665                Outcome::Err(e) => return Outcome::Err(e),
1666                Outcome::Cancelled(r) => return Outcome::Cancelled(r),
1667                Outcome::Panicked(p) => return Outcome::Panicked(p),
1668            };
1669            Outcome::Ok(rows.into_iter().next())
1670        }
1671    }
1672
1673    fn execute(
1674        &self,
1675        cx: &Cx,
1676        sql: &str,
1677        params: &[Value],
1678    ) -> impl Future<Output = Outcome<u64, Error>> + Send {
1679        let inner = Arc::clone(&self.inner);
1680        let sql = sql.to_string();
1681        let params = params.to_vec();
1682        async move {
1683            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1684                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1685            };
1686            guard.execute_async(cx, &sql, &params).await
1687        }
1688    }
1689
1690    fn savepoint(&self, cx: &Cx, name: &str) -> impl Future<Output = Outcome<(), Error>> + Send {
1691        let inner = Arc::clone(&self.inner);
1692        let name = name.to_string();
1693        async move {
1694            if let Err(e) = validate_savepoint_name(&name) {
1695                return Outcome::Err(e);
1696            }
1697            let sql = format!("SAVEPOINT {}", name);
1698            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1699                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1700            };
1701            guard.execute_async(cx, &sql, &[]).await.map(|_| ())
1702        }
1703    }
1704
1705    fn rollback_to(&self, cx: &Cx, name: &str) -> impl Future<Output = Outcome<(), Error>> + Send {
1706        let inner = Arc::clone(&self.inner);
1707        let name = name.to_string();
1708        async move {
1709            if let Err(e) = validate_savepoint_name(&name) {
1710                return Outcome::Err(e);
1711            }
1712            let sql = format!("ROLLBACK TO SAVEPOINT {}", name);
1713            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1714                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1715            };
1716            guard.execute_async(cx, &sql, &[]).await.map(|_| ())
1717        }
1718    }
1719
1720    fn release(&self, cx: &Cx, name: &str) -> impl Future<Output = Outcome<(), Error>> + Send {
1721        let inner = Arc::clone(&self.inner);
1722        let name = name.to_string();
1723        async move {
1724            if let Err(e) = validate_savepoint_name(&name) {
1725                return Outcome::Err(e);
1726            }
1727            let sql = format!("RELEASE SAVEPOINT {}", name);
1728            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1729                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1730            };
1731            guard.execute_async(cx, &sql, &[]).await.map(|_| ())
1732        }
1733    }
1734
1735    // Note: clippy sometimes flags `self.committed = true` as unused, but Drop reads it.
1736    #[allow(unused_assignments)]
1737    fn commit(mut self, cx: &Cx) -> impl Future<Output = Outcome<(), Error>> + Send {
1738        let inner = Arc::clone(&self.inner);
1739        async move {
1740            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1741                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1742            };
1743            let result = guard.execute_async(cx, "COMMIT", &[]).await;
1744            if matches!(result, Outcome::Ok(_)) {
1745                self.committed = true;
1746            }
1747            result.map(|_| ())
1748        }
1749    }
1750
1751    #[allow(unused_assignments)]
1752    fn rollback(mut self, cx: &Cx) -> impl Future<Output = Outcome<(), Error>> + Send {
1753        let inner = Arc::clone(&self.inner);
1754        async move {
1755            let Ok(mut guard) = OwnedMutexGuard::lock(Arc::clone(&inner), cx).await else {
1756                return Outcome::Err(connection_error("Failed to acquire connection lock"));
1757            };
1758            let result = guard.execute_async(cx, "ROLLBACK", &[]).await;
1759            if matches!(result, Outcome::Ok(_)) {
1760                self.committed = true;
1761            }
1762            result.map(|_| ())
1763        }
1764    }
1765}
1766
1767// ==================== Helpers ====================
1768
1769struct PgQueryResult {
1770    rows: Vec<Row>,
1771    command_tag: Option<String>,
1772}
1773
1774fn connection_error(msg: impl Into<String>) -> Error {
1775    Error::Connection(ConnectionError {
1776        kind: ConnectionErrorKind::Connect,
1777        message: msg.into(),
1778        source: None,
1779    })
1780}
1781
1782fn auth_error(msg: impl Into<String>) -> Error {
1783    Error::Connection(ConnectionError {
1784        kind: ConnectionErrorKind::Authentication,
1785        message: msg.into(),
1786        source: None,
1787    })
1788}
1789
1790fn protocol_error(msg: impl Into<String>) -> Error {
1791    Error::Protocol(ProtocolError {
1792        message: msg.into(),
1793        raw_data: None,
1794        source: None,
1795    })
1796}
1797
1798fn query_error_msg(msg: impl Into<String>, kind: QueryErrorKind) -> Error {
1799    Error::Query(QueryError {
1800        kind,
1801        message: msg.into(),
1802        sqlstate: None,
1803        sql: None,
1804        detail: None,
1805        hint: None,
1806        position: None,
1807        source: None,
1808    })
1809}
1810
1811fn error_from_fields(fields: &ErrorFields) -> Error {
1812    let kind = match fields.code.get(..2) {
1813        Some("08") => {
1814            return Error::Connection(ConnectionError {
1815                kind: ConnectionErrorKind::Connect,
1816                message: fields.message.clone(),
1817                source: None,
1818            });
1819        }
1820        Some("28") => {
1821            return Error::Connection(ConnectionError {
1822                kind: ConnectionErrorKind::Authentication,
1823                message: fields.message.clone(),
1824                source: None,
1825            });
1826        }
1827        Some("42") => QueryErrorKind::Syntax,
1828        Some("23") => QueryErrorKind::Constraint,
1829        Some("40") => {
1830            if fields.code == "40001" {
1831                QueryErrorKind::Serialization
1832            } else {
1833                QueryErrorKind::Deadlock
1834            }
1835        }
1836        Some("57") => {
1837            if fields.code == "57014" {
1838                QueryErrorKind::Cancelled
1839            } else {
1840                QueryErrorKind::Timeout
1841            }
1842        }
1843        _ => QueryErrorKind::Database,
1844    };
1845
1846    Error::Query(QueryError {
1847        kind,
1848        sql: None,
1849        sqlstate: Some(fields.code.clone()),
1850        message: fields.message.clone(),
1851        detail: fields.detail.clone(),
1852        hint: fields.hint.clone(),
1853        position: fields.position.map(|p| p as usize),
1854        source: None,
1855    })
1856}
1857
1858fn parse_rows_affected(tag: Option<&str>) -> Option<u64> {
1859    let tag = tag?;
1860    let mut parts = tag.split_whitespace().collect::<Vec<_>>();
1861    parts.pop().and_then(|last| last.parse::<u64>().ok())
1862}
1863
1864/// Validate a savepoint name to reduce SQL injection risk.
1865fn validate_savepoint_name(name: &str) -> sqlmodel_core::Result<()> {
1866    if name.is_empty() {
1867        return Err(query_error_msg(
1868            "Savepoint name cannot be empty",
1869            QueryErrorKind::Syntax,
1870        ));
1871    }
1872    if name.len() > 63 {
1873        return Err(query_error_msg(
1874            "Savepoint name exceeds maximum length of 63 characters",
1875            QueryErrorKind::Syntax,
1876        ));
1877    }
1878    let mut chars = name.chars();
1879    let Some(first) = chars.next() else {
1880        return Err(query_error_msg(
1881            "Savepoint name cannot be empty",
1882            QueryErrorKind::Syntax,
1883        ));
1884    };
1885    if !first.is_ascii_alphabetic() && first != '_' {
1886        return Err(query_error_msg(
1887            "Savepoint name must start with a letter or underscore",
1888            QueryErrorKind::Syntax,
1889        ));
1890    }
1891    for c in chars {
1892        if !c.is_ascii_alphanumeric() && c != '_' {
1893            return Err(query_error_msg(
1894                format!("Savepoint name contains invalid character: '{c}'"),
1895                QueryErrorKind::Syntax,
1896            ));
1897        }
1898    }
1899    Ok(())
1900}
1901
1902fn md5_password(user: &str, password: &str, salt: [u8; 4]) -> String {
1903    use std::fmt::Write;
1904
1905    let inner = format!("{password}{user}");
1906    let inner_hash = md5::compute(inner.as_bytes());
1907
1908    let mut outer_input = format!("{inner_hash:x}").into_bytes();
1909    outer_input.extend_from_slice(&salt);
1910    let outer_hash = md5::compute(&outer_input);
1911
1912    let mut result = String::with_capacity(35);
1913    result.push_str("md5");
1914    write!(&mut result, "{outer_hash:x}").unwrap();
1915    result
1916}
1917
1918// Note: read/write helpers are implemented above on PgAsyncStream.