Skip to main content

mz_pgwire/
protocol.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::collections::BTreeMap;
11use std::convert::TryFrom;
12use std::future::Future;
13use std::ops::Deref;
14use std::sync::Arc;
15use std::time::{Duration, Instant};
16use std::{iter, mem};
17
18use base64::prelude::*;
19use byteorder::{ByteOrder, NetworkEndian};
20use csv_core::ReadRecordResult;
21use futures::future::{BoxFuture, FutureExt, pending};
22use itertools::Itertools;
23use mz_adapter::client::{RecordFirstRowStream, redact_sql_for_logging};
24use mz_adapter::session::{
25    EndTransactionAction, InProgressRows, LifecycleTimestamps, PortalRefMut, PortalState, Session,
26    SessionConfig, TransactionStatus,
27};
28use mz_adapter::statement_logging::{StatementEndedExecutionReason, StatementExecutionStrategy};
29use mz_adapter::{
30    AdapterError, AdapterNotice, ExecuteContextGuard, ExecuteResponse, ExecutionTime,
31    PeekResponseUnary, metrics, verify_datum_desc,
32};
33use mz_adapter_types::dyncfgs::OIDC_GROUP_CLAIM;
34use mz_auth::Authenticated;
35use mz_auth::password::Password;
36use mz_authenticator::{Authenticator, GenericOidcAuthenticator};
37use mz_frontegg_auth::Authenticator as FronteggAuthenticator;
38use mz_ore::cast::CastFrom;
39use mz_ore::netio::AsyncReady;
40use mz_ore::now::{EpochMillis, SYSTEM_TIME};
41use mz_ore::str::StrExt;
42use mz_ore::{assert_none, assert_ok, instrument, soft_assert_eq_or_log, soft_assert_or_log};
43use mz_pgcopy::{CopyCsvFormatParams, CopyFormatParams, CopyTextFormatParams};
44use mz_pgwire_common::{
45    ConnectionCounter, Cursor, ErrorResponse, Format, FrontendMessage, Severity, VERSION_3,
46    VERSIONS,
47};
48use mz_repr::{
49    CatalogItemId, ColumnIndex, Datum, RelationDesc, RowArena, RowIterator, RowRef,
50    SqlRelationType, SqlScalarType,
51};
52use mz_server_core::TlsMode;
53use mz_server_core::listeners;
54use mz_server_core::listeners::AllowedRoles;
55use mz_sql::ast::display::AstDisplay;
56use mz_sql::ast::{
57    CopyDirection, CopyStatement, CopyTarget, FetchDirection, Ident, Raw, Statement,
58};
59use mz_sql::parse::StatementParseResult;
60use mz_sql::plan::{CopyFormat, ExecuteTimeout, StatementDesc};
61use mz_sql::session::metadata::SessionMetadata;
62use mz_sql::session::user::INTERNAL_USER_NAMES;
63use mz_sql::session::vars::VarInput;
64use postgres::error::SqlState;
65use tokio::io::{self, AsyncRead, AsyncWrite};
66use tokio::select;
67use tokio::time::{self};
68use tokio_metrics::TaskMetrics;
69use tracing::{Instrument, debug, debug_span, info, warn};
70use uuid::Uuid;
71
72use crate::codec::{
73    FramedConn, decode_password, decode_sasl_initial_response, decode_sasl_response,
74};
75use crate::message::{
76    self, BackendMessage, SASLServerFinalMessage, SASLServerFinalMessageKinds,
77    SASLServerFirstMessage,
78};
79
80/// Reports whether the given stream begins with a pgwire handshake.
81///
82/// To avoid false negatives, there must be at least eight bytes in `buf`.
83pub fn match_handshake(buf: &[u8]) -> bool {
84    // The pgwire StartupMessage looks like this:
85    //
86    //     i32 - Length of entire message.
87    //     i32 - Protocol version number.
88    //     [String] - Arbitrary key-value parameters of any length.
89    //
90    // Since arbitrary parameters can be included in the StartupMessage, the
91    // first Int32 is worthless, since the message could have any length.
92    // Instead, we sniff the protocol version number.
93    if buf.len() < 8 {
94        return false;
95    }
96    let version = NetworkEndian::read_i32(&buf[4..8]);
97    VERSIONS.contains(&version)
98}
99
100/// Parameters for the [`run`] function.
101pub struct RunParams<'a, A, I>
102where
103    I: Iterator<Item = TaskMetrics> + Send,
104{
105    /// The TLS mode of the pgwire server.
106    pub tls_mode: Option<TlsMode>,
107    /// A client for the adapter.
108    pub adapter_client: mz_adapter::Client,
109    /// The connection to the client.
110    pub conn: &'a mut FramedConn<A>,
111    /// The universally unique identifier for the connection.
112    pub conn_uuid: Uuid,
113    /// The protocol version that the client provided in the startup message.
114    pub version: i32,
115    /// The parameters that the client provided in the startup message.
116    pub params: BTreeMap<String, String>,
117    /// Frontegg JWT authenticator.
118    pub frontegg: Option<FronteggAuthenticator>,
119    /// OIDC authenticator.
120    pub oidc: GenericOidcAuthenticator,
121    /// The authentication method defined by the server's listener
122    /// configuration.
123    pub authenticator_kind: listeners::AuthenticatorKind,
124    /// Global connection limit and count
125    pub active_connection_counter: ConnectionCounter,
126    /// Helm chart version
127    pub helm_chart_version: Option<String>,
128    /// Whether to allow reserved users (ie: mz_system).
129    pub allowed_roles: AllowedRoles,
130    /// Tokio metrics
131    pub tokio_metrics_intervals: I,
132}
133
134/// Runs a pgwire connection to completion.
135///
136/// This involves responding to `FrontendMessage::StartupMessage` and all future
137/// requests until the client terminates the connection or a fatal error occurs.
138///
139/// Note that this function returns successfully even upon delivering a fatal
140/// error to the client. It only returns `Err` if an unexpected I/O error occurs
141/// while communicating with the client, e.g., if the connection is severed in
142/// the middle of a request.
143#[mz_ore::instrument(level = "debug")]
144pub async fn run<'a, A, I>(
145    RunParams {
146        tls_mode,
147        adapter_client,
148        conn,
149        conn_uuid,
150        version,
151        mut params,
152        frontegg,
153        oidc,
154        authenticator_kind,
155        active_connection_counter,
156        helm_chart_version,
157        allowed_roles,
158        tokio_metrics_intervals,
159    }: RunParams<'a, A, I>,
160) -> Result<(), io::Error>
161where
162    A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
163    I: Iterator<Item = TaskMetrics> + Send,
164{
165    if version != VERSION_3 {
166        return conn
167            .send(ErrorResponse::fatal(
168                SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
169                "server does not support the client's requested protocol version",
170            ))
171            .await;
172    }
173
174    let user = params.remove("user").unwrap_or_else(String::new);
175    let options = parse_options(params.get("options").unwrap_or(&String::new()));
176    let authenticator =
177        get_authenticator(authenticator_kind, frontegg, oidc, adapter_client.clone());
178    // TODO move this somewhere it can be shared with HTTP
179    let is_internal_user = INTERNAL_USER_NAMES.contains(&user);
180    // this is a superset of internal users
181    let is_reserved_user = mz_adapter::catalog::is_reserved_role_name(user.as_str());
182    let role_allowed = match allowed_roles {
183        AllowedRoles::Normal => !is_reserved_user,
184        AllowedRoles::Internal => is_internal_user,
185        AllowedRoles::NormalAndInternal => !is_reserved_user || is_internal_user,
186    };
187    if !role_allowed {
188        let msg = format!("unauthorized login to user '{user}'");
189        return conn
190            .send(ErrorResponse::fatal(SqlState::INSUFFICIENT_PRIVILEGE, msg))
191            .await;
192    }
193
194    if let Err(err) = conn.inner().ensure_tls_compatibility(&tls_mode) {
195        return conn.send(err).await;
196    }
197
198    let authenticator_kind = authenticator.kind();
199
200    let (mut session, expired) = match authenticator {
201        Authenticator::Frontegg(frontegg) => {
202            let password = match request_cleartext_password(conn).await {
203                Ok(password) => password,
204                Err(PasswordRequestError::IoError(e)) => return Err(e),
205                Err(PasswordRequestError::InvalidPasswordError(e)) => {
206                    return conn.send(e).await;
207                }
208            };
209
210            let group_claim =
211                OIDC_GROUP_CLAIM.get(adapter_client.get_system_vars().await.dyncfgs());
212            let auth_response = frontegg
213                .authenticate(&user, &password, Some(&group_claim))
214                .await;
215            match auth_response {
216                // Create a session based on the auth session.
217                //
218                // In particular, it's important that the username come from the
219                // auth session, as Frontegg may return an email address with
220                // different casing than the user supplied via the pgwire
221                // username fN
222                Ok((mut auth_session, authenticated)) => {
223                    let groups = auth_session.groups();
224                    let session = adapter_client.new_session(
225                        SessionConfig {
226                            conn_id: conn.conn_id().clone(),
227                            uuid: conn_uuid,
228                            user: auth_session.user().into(),
229                            client_ip: conn.peer_addr().clone(),
230                            external_metadata_rx: Some(auth_session.external_metadata_rx()),
231                            helm_chart_version,
232                            authenticator_kind,
233                            groups,
234                        },
235                        authenticated,
236                    );
237                    let expired = async move { auth_session.expired().await };
238                    (session, expired.left_future())
239                }
240                Err(err) => {
241                    warn!(?err, "pgwire connection failed authentication");
242                    return conn
243                        .send(ErrorResponse::fatal(
244                            SqlState::INVALID_PASSWORD,
245                            "invalid password",
246                        ))
247                        .await;
248                }
249            }
250        }
251        Authenticator::Oidc(oidc) => {
252            // OIDC listener: accepts either a JWT (uses OIDC authentication) or a
253            // plain SQL password (uses SQL password authentication).
254            let password = match request_cleartext_password(conn).await {
255                Ok(password) => password,
256                Err(PasswordRequestError::IoError(e)) => return Err(e),
257                Err(PasswordRequestError::InvalidPasswordError(e)) => {
258                    return conn.send(e).await;
259                }
260            };
261            if is_jwt(&password) {
262                let auth_response = oidc.authenticate(&password, Some(&user)).await;
263                match auth_response {
264                    Ok((mut claims, authenticated)) => {
265                        let groups = claims.groups.take();
266                        let session = adapter_client.new_session(
267                            SessionConfig {
268                                conn_id: conn.conn_id().clone(),
269                                uuid: conn_uuid,
270                                user: std::mem::take(&mut claims.user),
271                                client_ip: conn.peer_addr().clone(),
272                                external_metadata_rx: None,
273                                helm_chart_version,
274                                authenticator_kind,
275                                groups,
276                            },
277                            authenticated,
278                        );
279                        // No invalidation of the auth session once authenticated,
280                        // so auth session lasts indefinitely.
281                        (session, pending().right_future())
282                    }
283                    Err(err) => {
284                        warn!(?err, "pgwire connection failed authentication");
285                        return conn.send(err.into_response()).await;
286                    }
287                }
288            } else {
289                let session = match authenticate_with_password(
290                    conn,
291                    &adapter_client,
292                    user,
293                    Password(password),
294                    conn_uuid,
295                    helm_chart_version,
296                )
297                .await
298                {
299                    Ok(session) => session,
300                    Err(PasswordRequestError::IoError(e)) => return Err(e),
301                    Err(PasswordRequestError::InvalidPasswordError(e)) => {
302                        return conn.send(e).await;
303                    }
304                };
305                (session, pending().right_future())
306            }
307        }
308        Authenticator::Password(adapter_client) => {
309            let password = match request_cleartext_password(conn).await {
310                Ok(password) => password,
311                Err(PasswordRequestError::IoError(e)) => return Err(e),
312                Err(PasswordRequestError::InvalidPasswordError(e)) => {
313                    return conn.send(e).await;
314                }
315            };
316            let session = match authenticate_with_password(
317                conn,
318                &adapter_client,
319                user,
320                Password(password),
321                conn_uuid,
322                helm_chart_version,
323            )
324            .await
325            {
326                Ok(session) => session,
327                Err(PasswordRequestError::IoError(e)) => return Err(e),
328                Err(PasswordRequestError::InvalidPasswordError(e)) => {
329                    return conn.send(e).await;
330                }
331            };
332            // No frontegg check, so auth session lasts indefinitely.
333            (session, pending().right_future())
334        }
335        Authenticator::Sasl(adapter_client) => {
336            // Start the handshake
337            conn.send(BackendMessage::AuthenticationSASL).await?;
338            conn.flush().await?;
339            // Get the initial response indicating chosen mechanism
340            let (mechanism, initial_response) = match conn.recv().await? {
341                Some(FrontendMessage::RawAuthentication(data)) => {
342                    match decode_sasl_initial_response(Cursor::new(&data)).ok() {
343                        Some(FrontendMessage::SASLInitialResponse {
344                            gs2_header,
345                            mechanism,
346                            initial_response,
347                        }) => {
348                            // We do not support channel binding
349                            if gs2_header.channel_binding_enabled() {
350                                return conn
351                                    .send(ErrorResponse::fatal(
352                                        SqlState::PROTOCOL_VIOLATION,
353                                        "channel binding not supported",
354                                    ))
355                                    .await;
356                            }
357                            (mechanism, initial_response)
358                        }
359                        _ => {
360                            return conn
361                                .send(ErrorResponse::fatal(
362                                    SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
363                                    "expected SASLInitialResponse message",
364                                ))
365                                .await;
366                        }
367                    }
368                }
369                _ => {
370                    return conn
371                        .send(ErrorResponse::fatal(
372                            SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
373                            "expected SASLInitialResponse message",
374                        ))
375                        .await;
376                }
377            };
378
379            if mechanism != "SCRAM-SHA-256" {
380                return conn
381                    .send(ErrorResponse::fatal(
382                        SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
383                        "unsupported SASL mechanism",
384                    ))
385                    .await;
386            }
387
388            if initial_response.nonce.len() > 256 {
389                return conn
390                    .send(ErrorResponse::fatal(
391                        SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
392                        "nonce too long",
393                    ))
394                    .await;
395            }
396
397            let (server_first_message_raw, mock_hash) = match adapter_client
398                .generate_sasl_challenge(&user, &initial_response.nonce)
399                .await
400            {
401                Ok(response) => {
402                    let server_first_message_raw = format!(
403                        "r={},s={},i={}",
404                        response.nonce, response.salt, response.iteration_count
405                    );
406
407                    let client_key = [0u8; 32];
408                    let server_key = [1u8; 32];
409                    let mock_hash = format!(
410                        "SCRAM-SHA-256${}:{}${}:{}",
411                        response.iteration_count,
412                        response.salt,
413                        BASE64_STANDARD.encode(client_key),
414                        BASE64_STANDARD.encode(server_key)
415                    );
416
417                    conn.send(BackendMessage::AuthenticationSASLContinue(
418                        SASLServerFirstMessage {
419                            iteration_count: response.iteration_count,
420                            nonce: response.nonce,
421                            salt: response.salt,
422                        },
423                    ))
424                    .await?;
425                    conn.flush().await?;
426                    (server_first_message_raw, mock_hash)
427                }
428                Err(e) => {
429                    return conn.send(e.into_response(Severity::Fatal)).await;
430                }
431            };
432
433            let authenticated = match conn.recv().await? {
434                Some(FrontendMessage::RawAuthentication(data)) => {
435                    match decode_sasl_response(Cursor::new(&data)).ok() {
436                        Some(FrontendMessage::SASLResponse(response)) => {
437                            let auth_message = format!(
438                                "{},{},{}",
439                                initial_response.client_first_message_bare_raw,
440                                server_first_message_raw,
441                                response.client_final_message_bare_raw
442                            );
443                            if response.proof.len() > 1024 {
444                                return conn
445                                    .send(ErrorResponse::fatal(
446                                        SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
447                                        "proof too long",
448                                    ))
449                                    .await;
450                            }
451                            match adapter_client
452                                .verify_sasl_proof(
453                                    &user,
454                                    &response.proof,
455                                    &auth_message,
456                                    &mock_hash,
457                                )
458                                .await
459                            {
460                                Ok((proof_response, authenticated)) => {
461                                    conn.send(BackendMessage::AuthenticationSASLFinal(
462                                        SASLServerFinalMessage {
463                                            kind: SASLServerFinalMessageKinds::Verifier(
464                                                proof_response.verifier,
465                                            ),
466                                            extensions: vec![],
467                                        },
468                                    ))
469                                    .await?;
470                                    conn.flush().await?;
471                                    authenticated
472                                }
473                                Err(_) => {
474                                    return conn
475                                        .send(ErrorResponse::fatal(
476                                            SqlState::INVALID_PASSWORD,
477                                            "invalid password",
478                                        ))
479                                        .await;
480                                }
481                            }
482                        }
483                        _ => {
484                            return conn
485                                .send(ErrorResponse::fatal(
486                                    SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
487                                    "expected SASLResponse message",
488                                ))
489                                .await;
490                        }
491                    }
492                }
493                _ => {
494                    return conn
495                        .send(ErrorResponse::fatal(
496                            SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
497                            "expected SASLResponse message",
498                        ))
499                        .await;
500                }
501            };
502
503            let session = adapter_client.new_session(
504                SessionConfig {
505                    conn_id: conn.conn_id().clone(),
506                    uuid: conn_uuid,
507                    user,
508                    client_ip: conn.peer_addr().clone(),
509                    external_metadata_rx: None,
510                    helm_chart_version,
511                    authenticator_kind,
512                    groups: None,
513                },
514                authenticated,
515            );
516            // No frontegg check, so auth session lasts indefinitely.
517            let auth_session = pending().right_future();
518            (session, auth_session)
519        }
520
521        Authenticator::None => {
522            let session = adapter_client.new_session(
523                SessionConfig {
524                    conn_id: conn.conn_id().clone(),
525                    uuid: conn_uuid,
526                    user,
527                    client_ip: conn.peer_addr().clone(),
528                    external_metadata_rx: None,
529                    helm_chart_version,
530                    authenticator_kind,
531                    groups: None,
532                },
533                Authenticated,
534            );
535            // No frontegg check, so auth session lasts indefinitely.
536            let auth_session = pending().right_future();
537            (session, auth_session)
538        }
539    };
540
541    let system_vars = adapter_client.get_system_vars().await;
542    // Startup parameters that were successfully applied. They additionally
543    // become the session's default values below, once role defaults have been
544    // applied too.
545    let mut applied_params = vec![];
546    for (name, value) in params {
547        let settings = match name.as_str() {
548            "options" => match &options {
549                Ok(opts) => opts,
550                Err(()) => {
551                    session.add_notice(AdapterNotice::BadStartupSetting {
552                        name,
553                        reason: "could not parse".into(),
554                    });
555                    continue;
556                }
557            },
558            _ => &vec![(name, value)],
559        };
560        for (key, val) in settings {
561            const LOCAL: bool = false;
562            // TODO: Issuing an error here is better than what we did before
563            // (silently ignore errors on set), but erroring the connection
564            // might be the better behavior. We maybe need to support more
565            // options sent by psql and drivers before we can safely do this.
566            match session
567                .vars_mut()
568                .set(&system_vars, key, VarInput::Flat(val), LOCAL)
569            {
570                Ok(()) => applied_params.push((key.clone(), val.clone())),
571                Err(err) => {
572                    session.add_notice(AdapterNotice::BadStartupSetting {
573                        name: key.clone(),
574                        reason: err.to_string(),
575                    });
576                }
577            }
578        }
579    }
580    session
581        .vars_mut()
582        .end_transaction(EndTransactionAction::Commit);
583
584    let _guard = match active_connection_counter.allocate_connection(session.user()) {
585        Ok(drop_connection) => drop_connection,
586        Err(e) => {
587            let e: AdapterError = e.into();
588            return conn.send(e.into_response(Severity::Fatal)).await;
589        }
590    };
591
592    // Register session with adapter.
593    let mut adapter_client = match adapter_client.startup(session).await {
594        Ok(adapter_client) => adapter_client,
595        Err(e) => return conn.send(e.into_response(Severity::Fatal)).await,
596    };
597
598    // Make the startup parameters the session's default values, so that RESET
599    // and DISCARD ALL restore them rather than the server defaults. This
600    // matches PostgreSQL, where client-supplied startup parameters take
601    // precedence over role defaults (which startup registration applied) both
602    // as the current value and as the reset value. Connection poolers rely on
603    // this. For example, pgbouncer's default server_reset_query is DISCARD
604    // ALL, which must not rebind a pooled connection to the default database.
605    for (key, val) in applied_params {
606        if let Err(err) = adapter_client
607            .session()
608            .vars_mut()
609            .set_default(&key, VarInput::Flat(&val))
610        {
611            // Unexpected, since the same value was accepted by set() above.
612            mz_ore::soft_panic_or_log!("failed to apply startup parameter as default: {err:?}");
613        }
614    }
615
616    // Authentication succeeded, so the connection may now carry query traffic,
617    // whose frames are far larger than any credential.
618    conn.allow_post_auth_frames();
619
620    let mut buf = vec![BackendMessage::AuthenticationOk];
621    for var in adapter_client.session().vars().notify_set() {
622        buf.push(BackendMessage::ParameterStatus(var.name(), var.value()));
623    }
624    buf.push(BackendMessage::BackendKeyData {
625        conn_id: adapter_client.session().conn_id().unhandled(),
626        secret_key: adapter_client.session().secret_key(),
627    });
628    buf.extend(
629        adapter_client
630            .session()
631            .drain_notices()
632            .into_iter()
633            .map(|notice| BackendMessage::ErrorResponse(notice.into_response())),
634    );
635    buf.push(BackendMessage::ReadyForQuery(
636        adapter_client.session().transaction().into(),
637    ));
638    conn.send_all(buf).await?;
639    conn.flush().await?;
640
641    let machine = StateMachine {
642        conn,
643        adapter_client,
644        txn_needs_commit: false,
645        reverted_parameters: BTreeMap::new(),
646        tokio_metrics_intervals,
647    };
648
649    select! {
650        r = machine.run() => {
651            // Errors produced internally (like a malformed frame header) should send an
652            // error to the client informing them why the connection was closed. We still want to
653            // return the original error up the stack, though, so we skip error checking during conn
654            // operations.
655            if let Err(err) = &r {
656                let _ = conn
657                    .send(ErrorResponse::fatal(
658                        SqlState::CONNECTION_FAILURE,
659                        err.to_string(),
660                    ))
661                    .await;
662                let _ = conn.flush().await;
663            }
664            r
665        },
666        _ = expired => {
667            conn
668                .send(ErrorResponse::fatal(SqlState::INVALID_AUTHORIZATION_SPECIFICATION, "authentication expired"))
669                .await?;
670            conn.flush().await
671        }
672    }
673}
674
675/// Decides if a given password is a JWT by checking
676/// if we can decode its header.
677fn is_jwt(password: &str) -> bool {
678    jsonwebtoken::decode_header(password).is_ok()
679}
680
681/// Returns (name, value) session settings pairs from an options value.
682///
683/// From Postgres, see pg_split_opts in postinit.c and process_postgres_switches
684/// in postgres.c.
685fn parse_options(value: &str) -> Result<Vec<(String, String)>, ()> {
686    let opts = split_options(value);
687    let mut pairs = Vec::with_capacity(opts.len());
688    let mut seen_prefix = false;
689    for opt in opts {
690        if !seen_prefix {
691            if opt == "-c" {
692                seen_prefix = true;
693            } else {
694                let (key, val) = parse_option(&opt)?;
695                pairs.push((key.to_owned(), val.to_owned()));
696            }
697        } else {
698            let (key, val) = opt.split_once('=').ok_or(())?;
699            pairs.push((key.to_owned(), val.to_owned()));
700            seen_prefix = false;
701        }
702    }
703    Ok(pairs)
704}
705
706/// Returns the parsed key and value from option of the form `--key=value`, `-c
707/// key=value`, or `-ckey=value`. Keys replace `-` with `_`. Returns an error if
708/// there was some other prefix.
709fn parse_option(option: &str) -> Result<(&str, &str), ()> {
710    let (key, value) = option.split_once('=').ok_or(())?;
711    for prefix in &["-c", "--"] {
712        if let Some(key) = key.strip_prefix(prefix) {
713            return Ok((key, value));
714        }
715    }
716    Err(())
717}
718
719/// Splits value by any number of spaces except those preceded by `\`.
720fn split_options(value: &str) -> Vec<String> {
721    let mut strs = Vec::new();
722    // Need to build a string because of the escaping, so we can't simply
723    // subslice into value, and this isn't called enough to need to make it
724    // smart so it only builds a string if needed.
725    let mut current = String::new();
726    let mut was_slash = false;
727    for c in value.chars() {
728        was_slash = match c {
729            ' ' => {
730                if was_slash {
731                    current.push(' ');
732                } else if !current.is_empty() {
733                    // To ignore multiple spaces in a row, only push if current
734                    // is not empty.
735                    strs.push(std::mem::take(&mut current));
736                }
737                false
738            }
739            '\\' => {
740                if was_slash {
741                    // Two slashes in a row will add a slash and not escape the
742                    // next char.
743                    current.push('\\');
744                    false
745                } else {
746                    true
747                }
748            }
749            _ => {
750                current.push(c);
751                false
752            }
753        };
754    }
755    // A `\` at the end will be ignored.
756    if !current.is_empty() {
757        strs.push(current);
758    }
759    strs
760}
761
762enum PasswordRequestError {
763    InvalidPasswordError(ErrorResponse),
764    IoError(io::Error),
765}
766
767impl From<io::Error> for PasswordRequestError {
768    fn from(e: io::Error) -> Self {
769        PasswordRequestError::IoError(e)
770    }
771}
772
773/// Requests a cleartext password from a connection and returns it if it is valid.
774/// Sends an error response in the connection if the password
775/// is not valid.
776async fn request_cleartext_password<A>(
777    conn: &mut FramedConn<A>,
778) -> Result<String, PasswordRequestError>
779where
780    A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
781{
782    conn.send(BackendMessage::AuthenticationCleartextPassword)
783        .await?;
784    conn.flush().await?;
785
786    if let Some(message) = conn.recv().await? {
787        if let FrontendMessage::RawAuthentication(data) = message {
788            if let Some(FrontendMessage::Password { password }) =
789                decode_password(Cursor::new(&data)).ok()
790            {
791                return Ok(password);
792            }
793        }
794    }
795
796    Err(PasswordRequestError::InvalidPasswordError(
797        ErrorResponse::fatal(
798            SqlState::INVALID_AUTHORIZATION_SPECIFICATION,
799            "expected Password message",
800        ),
801    ))
802}
803
804/// Helper for password-based authentication using AdapterClient
805/// and returns an authenticated session.
806async fn authenticate_with_password<A>(
807    conn: &FramedConn<A>,
808    adapter_client: &mz_adapter::Client,
809    user: String,
810    password: Password,
811    conn_uuid: Uuid,
812    helm_chart_version: Option<String>,
813) -> Result<Session, PasswordRequestError>
814where
815    A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
816{
817    let authenticated = match adapter_client.authenticate(&user, &password).await {
818        Ok(authenticated) => authenticated,
819        Err(err) => {
820            warn!(?err, "pgwire connection failed authentication");
821            return Err(PasswordRequestError::InvalidPasswordError(
822                ErrorResponse::fatal(SqlState::INVALID_PASSWORD, "invalid password"),
823            ));
824        }
825    };
826
827    let session = adapter_client.new_session(
828        SessionConfig {
829            conn_id: conn.conn_id().clone(),
830            uuid: conn_uuid,
831            user,
832            client_ip: conn.peer_addr().clone(),
833            external_metadata_rx: None,
834            helm_chart_version,
835            authenticator_kind: mz_auth::AuthenticatorKind::Password,
836            groups: None,
837        },
838        authenticated,
839    );
840
841    Ok(session)
842}
843
844#[derive(Debug)]
845enum State {
846    Ready,
847    Drain,
848    Done,
849}
850
851struct StateMachine<'a, A, I>
852where
853    I: Iterator<Item = TaskMetrics> + Send + 'a,
854{
855    conn: &'a mut FramedConn<A>,
856    adapter_client: mz_adapter::SessionClient,
857    txn_needs_commit: bool,
858    /// Session parameters that a commit restored, to announce before the next `ReadyForQuery`.
859    reverted_parameters: BTreeMap<&'static str, String>,
860    tokio_metrics_intervals: I,
861}
862
863enum SendRowsEndedReason {
864    Success {
865        result_size: u64,
866        rows_returned: u64,
867    },
868    Errored {
869        error: String,
870    },
871    Canceled,
872}
873
874const ABORTED_TXN_MSG: &str =
875    "current transaction is aborted, commands ignored until end of transaction block";
876
877impl<'a, A, I> StateMachine<'a, A, I>
878where
879    A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin + 'a,
880    I: Iterator<Item = TaskMetrics> + Send + 'a,
881{
882    // Manually desugar this (don't use `async fn run`) here because a much better
883    // error message is produced if there are problems with Send or other traits
884    // somewhere within the Future.
885    #[allow(clippy::manual_async_fn)]
886    #[mz_ore::instrument(level = "debug")]
887    fn run(mut self) -> impl Future<Output = Result<(), io::Error>> + Send + 'a {
888        async move {
889            let mut state = State::Ready;
890            loop {
891                self.send_pending_notices().await?;
892                state = match state {
893                    State::Ready => self.advance_ready().await?,
894                    State::Drain => self.advance_drain().await?,
895                    State::Done => return Ok(()),
896                };
897                self.adapter_client
898                    .add_idle_in_transaction_session_timeout();
899            }
900        }
901    }
902
903    #[instrument(level = "debug")]
904    async fn advance_ready(&mut self) -> Result<State, io::Error> {
905        // Start a new metrics interval before the `recv()` call.
906        self.tokio_metrics_intervals
907            .next()
908            .expect("infinite iterator");
909
910        // Handle timeouts first so we don't execute any statements when there's a pending timeout.
911        let message = select! {
912            biased;
913
914            // `recv_timeout()` is cancel-safe as per it's docs.
915            Some(timeout) = self.adapter_client.recv_timeout() => {
916                let err: AdapterError = timeout.into();
917                let conn_id = self.adapter_client.session().conn_id();
918                tracing::warn!("session timed out, conn_id {}", conn_id);
919
920                // Process the error, doing any state cleanup.
921                let error_response = err.into_response(Severity::Fatal);
922                let error_state = self.send_error_and_get_state(error_response).await;
923
924                // Terminate __after__ we do any cleanup.
925                self.adapter_client.terminate().await;
926
927                // We must wait for the client to send a request before we can send the error response.
928                // Due to the PG wire protocol, we can't send an ErrorResponse unless it is in response
929                // to a client message.
930                let _ = self.conn.recv().await?;
931                return error_state;
932            },
933            // `recv()` is cancel-safe as per it's docs.
934            message = self.conn.recv() => message?,
935        };
936
937        // Take the metrics since just before the `recv`.
938        let interval = self
939            .tokio_metrics_intervals
940            .next()
941            .expect("infinite iterator");
942        let recv_scheduling_delay_ms = interval.total_scheduled_duration.as_secs_f64() * 1000.0;
943
944        // TODO(ggevay): Consider subtracting the scheduling delay from `received`. It's not obvious
945        // whether we should do this, because the result wouldn't exactly correspond to either first
946        // byte received or last byte received (for msgs that arrive in more than one network packet).
947        let received = SYSTEM_TIME();
948
949        self.adapter_client
950            .remove_idle_in_transaction_session_timeout();
951
952        // NOTE(guswynn): we could consider adding spans to all message types. Currently
953        // only a few message types seem useful.
954        let message_name = message.as_ref().map(|m| m.name()).unwrap_or_default();
955
956        if let Some(message) = &message {
957            self.maybe_log_message_arrival(message).await;
958        }
959
960        let start = message.as_ref().map(|_| Instant::now());
961        let next_state = match message {
962            Some(FrontendMessage::Query { sql }) => {
963                let query_root_span =
964                    tracing::info_span!(parent: None, "advance_ready", otel.name = message_name);
965                query_root_span.follows_from(tracing::Span::current());
966                self.query(sql, received)
967                    .instrument(query_root_span)
968                    .await?
969            }
970            Some(FrontendMessage::Parse {
971                name,
972                sql,
973                param_types,
974            }) => self.parse(name, sql, param_types).await?,
975            Some(FrontendMessage::Bind {
976                portal_name,
977                statement_name,
978                param_formats,
979                raw_params,
980                result_formats,
981            }) => {
982                self.bind(
983                    portal_name,
984                    statement_name,
985                    param_formats,
986                    raw_params,
987                    result_formats,
988                )
989                .await?
990            }
991            Some(FrontendMessage::Execute {
992                portal_name,
993                max_rows,
994            }) => {
995                let max_rows = match usize::try_from(max_rows) {
996                    Ok(0) | Err(_) => ExecuteCount::All, // If `max_rows < 0`, no limit.
997                    Ok(n) => ExecuteCount::Count(n),
998                };
999                let execute_root_span =
1000                    tracing::info_span!(parent: None, "advance_ready", otel.name = message_name);
1001                execute_root_span.follows_from(tracing::Span::current());
1002                let state = self
1003                    .execute(
1004                        portal_name,
1005                        max_rows,
1006                        portal_exec_message,
1007                        None,
1008                        ExecuteTimeout::None,
1009                        None,
1010                        Some(received),
1011                        // The extended protocol commits after `CommandComplete`, at `Sync` or
1012                        // when the next statement starts (`txn_needs_commit` below).
1013                        false,
1014                    )
1015                    .instrument(execute_root_span)
1016                    .await?;
1017                // In PostgreSQL, when using the extended query protocol, some statements may
1018                // trigger an eager commit of the current implicit transaction,
1019                // see: <https://git.postgresql.org/gitweb/?p=postgresql.git&a=commitdiff&h=f92944137>.
1020                //
1021                // In Materialize we instead eagerly commit every implicit transaction that
1022                // cannot take on further statements of the same pipeline, which keeps the
1023                // single-statement optimizations available to queries issued in the extended
1024                // query protocol. The ones that can stay open, so that the pipeline commits
1025                // or rolls back as a unit. See `TransactionStatus::may_span_pipeline`.
1026                //
1027                // We don't immediately commit here to allow users to page through the portal if
1028                // necessary. Committing the transaction would destroy the portal before the next
1029                // Execute command has a chance to resume it. So we instead mark the transaction
1030                // for commit the next time that `ensure_transaction` is called.
1031                let (is_implicit, may_span_pipeline) = {
1032                    let txn = self.adapter_client.session().transaction();
1033                    (txn.is_implicit(), txn.may_span_pipeline())
1034                };
1035                // Ordered so that only a write reads the flag, keeping the catalog
1036                // snapshot off the read path.
1037                let spans_pipeline = may_span_pipeline
1038                    && self
1039                        .adapter_client
1040                        .extended_protocol_implicit_transaction_enabled()
1041                        .await;
1042                if is_implicit && !spans_pipeline {
1043                    self.txn_needs_commit = true;
1044                }
1045                state
1046            }
1047            Some(FrontendMessage::DescribeStatement { name }) => {
1048                self.describe_statement(&name).await?
1049            }
1050            Some(FrontendMessage::DescribePortal { name }) => self.describe_portal(&name).await?,
1051            Some(FrontendMessage::CloseStatement { name }) => self.close_statement(name).await?,
1052            Some(FrontendMessage::ClosePortal { name }) => self.close_portal(name).await?,
1053            Some(FrontendMessage::Flush) => self.flush().await?,
1054            Some(FrontendMessage::Sync) => self.sync().await?,
1055            Some(FrontendMessage::Terminate) => State::Done,
1056
1057            // Accept but ignore stray COPY subprotocol messages, mirroring
1058            // PostgreSQL. Clients stream COPY data optimistically, so when a
1059            // COPY statement fails before COPY mode is entered, its pipelined
1060            // CopyData/CopyDone/CopyFail arrive here. Draining instead would
1061            // discard unrelated messages until the next Sync, hanging simple
1062            // protocol clients that never send one.
1063            Some(FrontendMessage::CopyData(_))
1064            | Some(FrontendMessage::CopyDone)
1065            | Some(FrontendMessage::CopyFail(_)) => State::Ready,
1066
1067            Some(FrontendMessage::Password { .. })
1068            | Some(FrontendMessage::RawAuthentication(_))
1069            | Some(FrontendMessage::SASLInitialResponse { .. })
1070            | Some(FrontendMessage::SASLResponse(_)) => State::Drain,
1071            None => State::Done,
1072        };
1073
1074        if let Some(start) = start {
1075            self.adapter_client
1076                .inner()
1077                .metrics()
1078                .pgwire_message_processing_seconds
1079                .with_label_values(&[message_name])
1080                .observe(start.elapsed().as_secs_f64());
1081        }
1082        self.adapter_client
1083            .inner()
1084            .metrics()
1085            .pgwire_recv_scheduling_delay_ms
1086            .with_label_values(&[message_name])
1087            .observe(recv_scheduling_delay_ms);
1088
1089        Ok(next_state)
1090    }
1091
1092    async fn advance_drain(&mut self) -> Result<State, io::Error> {
1093        let message = self.conn.recv().await?;
1094        if message.is_some() {
1095            self.adapter_client
1096                .remove_idle_in_transaction_session_timeout();
1097        }
1098        match message {
1099            Some(FrontendMessage::Sync) => self.sync().await,
1100            None => Ok(State::Done),
1101            _ => Ok(State::Drain),
1102        }
1103    }
1104
1105    /// Note that `lifecycle_timestamps` belongs to the whole "Simple Query", because the whole
1106    /// Simple Query is received and parsed together. This means that if there are multiple
1107    /// statements in a Simple Query, then all of them have the same `lifecycle_timestamps`.
1108    #[instrument(level = "debug")]
1109    async fn one_query(
1110        &mut self,
1111        stmt: Statement<Raw>,
1112        sql: String,
1113        lifecycle_timestamps: LifecycleTimestamps,
1114        ends_simple_query: bool,
1115    ) -> Result<State, io::Error> {
1116        // Bind the portal. Note that this does not set the empty string prepared
1117        // statement.
1118        const EMPTY_PORTAL: &str = "";
1119        if let Err(e) = self
1120            .adapter_client
1121            .declare(EMPTY_PORTAL.to_string(), stmt, sql)
1122            .await
1123        {
1124            return self
1125                .send_error_and_get_state(e.into_response(Severity::Error))
1126                .await;
1127        }
1128        let portal = self
1129            .adapter_client
1130            .session()
1131            .get_portal_unverified_mut(EMPTY_PORTAL)
1132            .expect("unnamed portal should be present");
1133
1134        *portal.lifecycle_timestamps = Some(lifecycle_timestamps);
1135
1136        let stmt_desc = portal.desc.clone();
1137        if !stmt_desc.param_types.is_empty() {
1138            return self
1139                .send_error_and_get_state(ErrorResponse::error(
1140                    SqlState::UNDEFINED_PARAMETER,
1141                    "there is no parameter $1",
1142                ))
1143                .await;
1144        }
1145
1146        // Maybe send row description.
1147        if let Some(relation_desc) = &stmt_desc.relation_desc {
1148            if !stmt_desc.is_copy {
1149                let formats = vec![Format::Text; stmt_desc.arity()];
1150                self.send(BackendMessage::RowDescription(
1151                    message::encode_row_description(relation_desc, &formats),
1152                ))
1153                .await?;
1154            }
1155        }
1156
1157        let result = match self
1158            .adapter_client
1159            .execute(EMPTY_PORTAL.to_string(), self.conn.wait_closed(), None)
1160            .await
1161        {
1162            Ok((response, execute_started)) => {
1163                self.send_execute_response(
1164                    response,
1165                    stmt_desc.relation_desc,
1166                    EMPTY_PORTAL.to_string(),
1167                    ExecuteCount::All,
1168                    portal_exec_message,
1169                    None,
1170                    ExecuteTimeout::None,
1171                    execute_started,
1172                    ends_simple_query,
1173                )
1174                .await
1175            }
1176            Err(e) => {
1177                self.send_pending_notices().await?;
1178                self.send_error_and_get_state(e.into_response(Severity::Error))
1179                    .await
1180            }
1181        };
1182
1183        // Destroy the portal.
1184        self.adapter_client.session().remove_portal(EMPTY_PORTAL);
1185
1186        result
1187    }
1188
1189    async fn ensure_transaction(
1190        &mut self,
1191        num_stmts: usize,
1192        message_type: &str,
1193    ) -> Result<(), io::Error> {
1194        let start = Instant::now();
1195        if self.txn_needs_commit {
1196            self.commit_transaction().await?;
1197        }
1198        // start_transaction can't error (but assert that just in case it changes in
1199        // the future.
1200        let res = self.adapter_client.start_transaction(Some(num_stmts));
1201        assert_ok!(res);
1202        self.adapter_client
1203            .inner()
1204            .metrics()
1205            .pgwire_ensure_transaction_seconds
1206            .with_label_values(&[message_type])
1207            .observe(start.elapsed().as_secs_f64());
1208        Ok(())
1209    }
1210
1211    /// Logs an arriving frontend message at info level, when
1212    /// `enable_statement_arrival_logging` is on. Runs before the message is
1213    /// processed, so a message whose processing crashes the process still
1214    /// appears in the log. The `kind` field says which message it is, and
1215    /// thereby also whether the statement came in through the simple protocol
1216    /// (`query`) or the extended protocol (`parse`, `bind`, `execute`, ...).
1217    /// The prepared statement and portal names, together with the connection
1218    /// id, allow connecting a `bind` or `execute` back to the `parse` that
1219    /// carried the SQL text.
1220    ///
1221    /// SQL text is parsed and logged with its literals redacted, the same
1222    /// redaction the statement log applies. This means a statement that
1223    /// crashes the parser is not captured, an accepted limitation. Bind
1224    /// parameter values are data that redaction cannot reach, so only their
1225    /// count is logged. Authentication payloads are never logged. COPY data
1226    /// is logged as its length only, and only when it arrives as a stray
1227    /// message in the ready state: messages consumed by the COPY subprotocol
1228    /// or the post-error drain loop don't pass through here at all.
1229    async fn maybe_log_message_arrival(&mut self, message: &FrontendMessage) {
1230        if !self
1231            .adapter_client
1232            .statement_arrival_logging_enabled()
1233            .await
1234        {
1235            return;
1236        }
1237        let session = self.adapter_client.session();
1238        let conn_id = session.conn_id();
1239        let session_uuid = session.uuid();
1240        let kind = message.name();
1241        match message {
1242            FrontendMessage::Query { sql } => {
1243                info!(
1244                    %conn_id, %session_uuid, kind, sql = %redact_sql_for_logging(sql),
1245                    "statement arrival"
1246                );
1247            }
1248            FrontendMessage::Parse { name, sql, .. } => {
1249                info!(
1250                    %conn_id, %session_uuid, kind, name, sql = %redact_sql_for_logging(sql),
1251                    "statement arrival"
1252                );
1253            }
1254            FrontendMessage::Bind {
1255                portal_name,
1256                statement_name,
1257                raw_params,
1258                ..
1259            } => {
1260                info!(
1261                    %conn_id, %session_uuid, kind, portal_name, statement_name,
1262                    num_params = raw_params.len(),
1263                    "statement arrival"
1264                );
1265            }
1266            // COPY payloads would flood the log. Log only their length.
1267            FrontendMessage::CopyData(data) => {
1268                info!(%conn_id, %session_uuid, kind, len = data.len(), "statement arrival");
1269            }
1270            // Authentication payloads must never be logged.
1271            FrontendMessage::Password { .. }
1272            | FrontendMessage::RawAuthentication(_)
1273            | FrontendMessage::SASLInitialResponse { .. }
1274            | FrontendMessage::SASLResponse(_) => {
1275                info!(%conn_id, %session_uuid, kind, "statement arrival");
1276            }
1277            // CopyFail carries a client-supplied free-text error message,
1278            // which we don't log.
1279            FrontendMessage::CopyFail(_) => {
1280                info!(%conn_id, %session_uuid, kind, "statement arrival");
1281            }
1282            // Log the full Debug representation for all other variants, which
1283            // carry only object names or no payload.
1284            FrontendMessage::DescribeStatement { .. }
1285            | FrontendMessage::DescribePortal { .. }
1286            | FrontendMessage::Execute { .. }
1287            | FrontendMessage::Flush
1288            | FrontendMessage::Sync
1289            | FrontendMessage::CloseStatement { .. }
1290            | FrontendMessage::ClosePortal { .. }
1291            | FrontendMessage::Terminate
1292            | FrontendMessage::CopyDone => {
1293                // WARNING: When adding a variant here, consider whether its payload is sensitive or
1294                // bulky!
1295                //
1296                // (The field must not be named `message`, that name is
1297                // reserved for the event text in tracing.)
1298                info!(%conn_id, %session_uuid, kind, contents = ?message, "statement arrival");
1299            }
1300        }
1301    }
1302
1303    fn parse_sql<'b>(&self, sql: &'b str) -> Result<Vec<StatementParseResult<'b>>, ErrorResponse> {
1304        let parse_start = Instant::now();
1305        let result = match self.adapter_client.parse(sql) {
1306            Ok(result) => result.map_err(|e| {
1307                // Convert our 0-based byte position to pgwire's 1-based character
1308                // position.
1309                let pos = sql[..e.error.pos].chars().count() + 1;
1310                ErrorResponse::error(SqlState::SYNTAX_ERROR, e.error.message).with_position(pos)
1311            }),
1312            Err(msg) => Err(ErrorResponse::error(SqlState::PROGRAM_LIMIT_EXCEEDED, msg)),
1313        };
1314        self.adapter_client
1315            .inner()
1316            .metrics()
1317            .parse_seconds
1318            .observe(parse_start.elapsed().as_secs_f64());
1319        result
1320    }
1321
1322    /// Executes a "Simple Query", see
1323    /// <https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-SIMPLE-QUERY>
1324    ///
1325    /// For implicit transaction handling, see "Multiple Statements in a Simple Query" in the above.
1326    #[instrument(level = "debug")]
1327    async fn query(&mut self, sql: String, received: EpochMillis) -> Result<State, io::Error> {
1328        // Parse first before doing any transaction checking.
1329        let stmts = match self.parse_sql(&sql) {
1330            Ok(stmts) => stmts,
1331            Err(err) => {
1332                self.send_error_and_get_state(err).await?;
1333                return self.ready().await;
1334            }
1335        };
1336
1337        let num_stmts = stmts.len();
1338
1339        // Compare with postgres' backend/tcop/postgres.c exec_simple_query.
1340        for (idx, StatementParseResult { ast: stmt, sql }) in stmts.into_iter().enumerate() {
1341            // In an aborted transaction, reject all commands except COMMIT/ROLLBACK.
1342            if self.is_aborted_txn() && !is_txn_exit_stmt(Some(&stmt)) {
1343                self.aborted_txn_error().await?;
1344                break;
1345            }
1346
1347            // Start an implicit transaction if we aren't in any transaction and there's
1348            // more than one statement. This mirrors the `use_implicit_block` variable in
1349            // postgres.
1350            //
1351            // This needs to be done in the loop instead of once at the top because
1352            // a COMMIT/ROLLBACK statement needs to start a new transaction on next
1353            // statement.
1354            self.ensure_transaction(num_stmts, "query").await?;
1355
1356            let ends_simple_query = idx + 1 == num_stmts;
1357            match self
1358                .one_query(
1359                    stmt,
1360                    sql.to_string(),
1361                    LifecycleTimestamps { received },
1362                    ends_simple_query,
1363                )
1364                .await?
1365            {
1366                State::Ready => (),
1367                State::Drain => break,
1368                State::Done => return Ok(State::Done),
1369            }
1370        }
1371
1372        // Implicit transactions are closed at the end of a Query message.
1373        {
1374            if self.adapter_client.session().transaction().is_implicit() {
1375                self.commit_transaction().await?;
1376            }
1377        }
1378
1379        if num_stmts == 0 {
1380            self.send(BackendMessage::EmptyQueryResponse).await?;
1381        }
1382
1383        self.ready().await
1384    }
1385
1386    #[instrument(level = "debug")]
1387    async fn parse(
1388        &mut self,
1389        name: String,
1390        sql: String,
1391        param_oids: Vec<u32>,
1392    ) -> Result<State, io::Error> {
1393        // Start a transaction if we aren't in one.
1394        self.ensure_transaction(1, "parse").await?;
1395
1396        let mut param_types = vec![];
1397        for oid in param_oids {
1398            match mz_pgrepr::Type::from_oid(oid) {
1399                Ok(ty) => match SqlScalarType::try_from(&ty) {
1400                    Ok(ty) => param_types.push(Some(ty)),
1401                    Err(err) => {
1402                        return self
1403                            .send_error_and_get_state(ErrorResponse::error(
1404                                SqlState::INVALID_PARAMETER_VALUE,
1405                                err.to_string(),
1406                            ))
1407                            .await;
1408                    }
1409                },
1410                Err(_) if oid == 0 => param_types.push(None),
1411                Err(e) => {
1412                    return self
1413                        .send_error_and_get_state(ErrorResponse::error(
1414                            SqlState::PROTOCOL_VIOLATION,
1415                            e.to_string(),
1416                        ))
1417                        .await;
1418                }
1419            }
1420        }
1421
1422        let stmts = match self.parse_sql(&sql) {
1423            Ok(stmts) => stmts,
1424            Err(err) => {
1425                return self.send_error_and_get_state(err).await;
1426            }
1427        };
1428        if stmts.len() > 1 {
1429            return self
1430                .send_error_and_get_state(ErrorResponse::error(
1431                    SqlState::INTERNAL_ERROR,
1432                    "cannot insert multiple commands into a prepared statement",
1433                ))
1434                .await;
1435        }
1436        let (maybe_stmt, sql) = match stmts.into_iter().next() {
1437            None => (None, ""),
1438            Some(StatementParseResult { ast, sql }) => (Some(ast), sql),
1439        };
1440        if self.is_aborted_txn() && !is_txn_exit_stmt(maybe_stmt.as_ref()) {
1441            return self.aborted_txn_error().await;
1442        }
1443        match self
1444            .adapter_client
1445            .prepare(name, maybe_stmt, sql.to_string(), param_types)
1446            .await
1447        {
1448            Ok(()) => {
1449                self.send(BackendMessage::ParseComplete).await?;
1450                Ok(State::Ready)
1451            }
1452            Err(e) => {
1453                self.send_error_and_get_state(e.into_response(Severity::Error))
1454                    .await
1455            }
1456        }
1457    }
1458
1459    /// Commits and clears the current transaction.
1460    #[instrument(level = "debug")]
1461    async fn commit_transaction(&mut self) -> Result<(), io::Error> {
1462        self.end_transaction(EndTransactionAction::Commit).await
1463    }
1464
1465    /// Rollback and clears the current transaction.
1466    #[instrument(level = "debug")]
1467    async fn rollback_transaction(&mut self) -> Result<(), io::Error> {
1468        self.end_transaction(EndTransactionAction::Rollback).await
1469    }
1470
1471    /// End a transaction and report to the user if an error occurred.
1472    ///
1473    /// The parameters this changes must be announced, exactly as an explicit
1474    /// `COMMIT`/`ROLLBACK` announces them. Otherwise a `SET LOCAL` outside an
1475    /// explicit transaction announces its new value and never its revert, and a
1476    /// client that caches parameters keeps the reverted value.
1477    #[instrument(level = "debug")]
1478    async fn end_transaction(&mut self, action: EndTransactionAction) -> Result<(), io::Error> {
1479        self.txn_needs_commit = false;
1480        match self.adapter_client.end_transaction(action).await {
1481            Ok(
1482                ExecuteResponse::TransactionCommitted { params }
1483                | ExecuteResponse::TransactionRolledBack { params },
1484            ) => {
1485                self.send_parameter_statuses(params).await?;
1486            }
1487            Ok(_) => {}
1488            Err(err) if action == EndTransactionAction::Commit => {
1489                self.send(BackendMessage::ErrorResponse(
1490                    err.into_response(Severity::Error),
1491                ))
1492                .await?;
1493            }
1494            // We only roll back after an error was already sent, and a client expects just one
1495            // error per failed query.
1496            Err(_) => {}
1497        }
1498        Ok(())
1499    }
1500
1501    /// Settles a statement, or a step of one, right before its completion message
1502    /// (`CommandComplete` or `PortalSuspended`). If `ends_simple_query`, the statement is the last
1503    /// of a simple query, and its implicit transaction commits now. Returns the commit's error,
1504    /// which takes the place of the completion message. The parameters that the commit reverts are
1505    /// announced before the next `ReadyForQuery`.
1506    async fn settle_statement(&mut self, ends_simple_query: bool) -> Option<ErrorResponse> {
1507        match self
1508            .adapter_client
1509            .settle_statement(ends_simple_query)
1510            .await
1511        {
1512            Ok(settled) => {
1513                self.reverted_parameters.extend(settled.reverted);
1514                None
1515            }
1516            Err(err) => Some(err.into_response(Severity::Error)),
1517        }
1518    }
1519
1520    async fn send_execution_time(&mut self, time: Option<ExecutionTime>) -> Result<(), io::Error> {
1521        if let Some(notice) =
1522            time.and_then(|time| self.adapter_client.session().execution_time_notice(time))
1523        {
1524            self.send(notice.into_response()).await?;
1525        }
1526        Ok(())
1527    }
1528
1529    /// Announces changed parameters, restricted to those the client is told
1530    /// about at startup.
1531    #[instrument(level = "debug")]
1532    async fn send_parameter_statuses(
1533        &mut self,
1534        params: BTreeMap<&'static str, String>,
1535    ) -> Result<(), io::Error> {
1536        let notify_set: mz_ore::collections::HashSet<String> = self
1537            .adapter_client
1538            .session()
1539            .vars()
1540            .notify_set()
1541            .map(|v| v.name().to_string())
1542            .collect();
1543
1544        for (name, value) in params
1545            .into_iter()
1546            .filter(|(name, _value)| notify_set.contains(*name))
1547        {
1548            self.send(BackendMessage::ParameterStatus(name, value))
1549                .await?;
1550        }
1551        Ok(())
1552    }
1553
1554    #[instrument(level = "debug")]
1555    async fn bind(
1556        &mut self,
1557        portal_name: String,
1558        statement_name: String,
1559        param_formats: Vec<Format>,
1560        raw_params: Vec<Option<Vec<u8>>>,
1561        result_formats: Vec<Format>,
1562    ) -> Result<State, io::Error> {
1563        // Start a transaction if we aren't in one.
1564        self.ensure_transaction(1, "bind").await?;
1565
1566        let aborted_txn = self.is_aborted_txn();
1567        let stmt = match self
1568            .adapter_client
1569            .get_prepared_statement(&statement_name)
1570            .await
1571        {
1572            Ok(stmt) => stmt,
1573            Err(err) => {
1574                return self
1575                    .send_error_and_get_state(err.into_response(Severity::Error))
1576                    .await;
1577            }
1578        };
1579
1580        let param_types = &stmt.desc().param_types;
1581        if param_types.len() != raw_params.len() {
1582            let message = format!(
1583                "bind message supplies {actual} parameters, \
1584                 but prepared statement \"{name}\" requires {expected}",
1585                name = statement_name,
1586                actual = raw_params.len(),
1587                expected = param_types.len()
1588            );
1589            return self
1590                .send_error_and_get_state(ErrorResponse::error(
1591                    SqlState::PROTOCOL_VIOLATION,
1592                    message,
1593                ))
1594                .await;
1595        }
1596        let param_formats = match pad_formats(param_formats, raw_params.len()) {
1597            Ok(param_formats) => param_formats,
1598            Err(msg) => {
1599                return self
1600                    .send_error_and_get_state(ErrorResponse::error(
1601                        SqlState::PROTOCOL_VIOLATION,
1602                        msg,
1603                    ))
1604                    .await;
1605            }
1606        };
1607        if aborted_txn && !is_txn_exit_stmt(stmt.stmt()) {
1608            return self.aborted_txn_error().await;
1609        }
1610        let buf = RowArena::new();
1611        let mut params = vec![];
1612        for ((raw_param, mz_typ), format) in raw_params
1613            .into_iter()
1614            .zip_eq(param_types)
1615            .zip_eq(param_formats)
1616        {
1617            let pg_typ = mz_pgrepr::Type::from(mz_typ);
1618            let datum = match raw_param {
1619                None => Datum::Null,
1620                Some(bytes) => match mz_pgrepr::Value::decode(format, &pg_typ, &bytes) {
1621                    Ok(param) => match param.into_datum_decode_error(&buf, &pg_typ, "parameter") {
1622                        Ok(datum) => datum,
1623                        Err(msg) => {
1624                            return self
1625                                .send_error_and_get_state(ErrorResponse::error(
1626                                    SqlState::INVALID_PARAMETER_VALUE,
1627                                    msg,
1628                                ))
1629                                .await;
1630                        }
1631                    },
1632                    Err(err) => {
1633                        // NUL characters get the same SQLSTATE that PostgreSQL
1634                        // reports for them.
1635                        let (code, msg) = if err.is::<mz_pgrepr::NulCharacterError>() {
1636                            (SqlState::CHARACTER_NOT_IN_REPERTOIRE, err.to_string())
1637                        } else {
1638                            (
1639                                SqlState::INVALID_PARAMETER_VALUE,
1640                                format!("unable to decode parameter: {}", err),
1641                            )
1642                        };
1643                        return self
1644                            .send_error_and_get_state(ErrorResponse::error(code, msg))
1645                            .await;
1646                    }
1647                },
1648            };
1649            params.push((datum, mz_typ.clone()))
1650        }
1651
1652        let result_formats = match pad_formats(
1653            result_formats,
1654            stmt.desc()
1655                .relation_desc
1656                .clone()
1657                .map(|desc| desc.typ().column_types.len())
1658                .unwrap_or(0),
1659        ) {
1660            Ok(result_formats) => result_formats,
1661            Err(msg) => {
1662                return self
1663                    .send_error_and_get_state(ErrorResponse::error(
1664                        SqlState::PROTOCOL_VIOLATION,
1665                        msg,
1666                    ))
1667                    .await;
1668            }
1669        };
1670
1671        // Binary encodings are disabled for list, map, and aclitem types, but this doesn't
1672        // apply to COPY TO statements.
1673        if !stmt.stmt().map_or(false, |stmt| match stmt {
1674            Statement::Copy(CopyStatement {
1675                direction: CopyDirection::To,
1676                ..
1677            }) => true,
1678            Statement::Copy(CopyStatement {
1679                direction: CopyDirection::From,
1680                // To be conservative, we are restricting COPY FROM to only allow list/map/aclitem types if it is not
1681                // copying from STDIN. It is likely that this works in theory, but is risky and likely to OOM anyways
1682                // as all the data will be held in a buffer in memory before being processed.
1683                target: CopyTarget::Expr(_),
1684                ..
1685            }) => true,
1686            _ => false,
1687        }) {
1688            if let Some(desc) = stmt.desc().relation_desc.clone() {
1689                for (format, ty) in result_formats.iter().zip_eq(desc.iter_types()) {
1690                    if let Format::Binary = format {
1691                        if let Err(msg) = mz_pgrepr::Value::binary_encoding_error(&ty.scalar_type) {
1692                            return self
1693                                .send_error_and_get_state(ErrorResponse::error(
1694                                    SqlState::UNDEFINED_FUNCTION,
1695                                    msg,
1696                                ))
1697                                .await;
1698                        }
1699                    }
1700                }
1701            }
1702        }
1703
1704        let desc = stmt.desc().clone();
1705        let logging = Arc::clone(stmt.logging());
1706        let stmt_ast = stmt.stmt().cloned();
1707        let state_revision = stmt.state_revision;
1708        if let Err(err) = self.adapter_client.session().set_portal(
1709            portal_name,
1710            desc,
1711            stmt_ast,
1712            logging,
1713            params,
1714            result_formats,
1715            state_revision,
1716        ) {
1717            return self
1718                .send_error_and_get_state(err.into_response(Severity::Error))
1719                .await;
1720        }
1721
1722        self.send(BackendMessage::BindComplete).await?;
1723        Ok(State::Ready)
1724    }
1725
1726    /// `outer_ctx_extra` is Some when we are executing as part of an outer statement, e.g., a FETCH
1727    /// triggering the execution of the underlying query.
1728    fn execute(
1729        &mut self,
1730        portal_name: String,
1731        max_rows: ExecuteCount,
1732        get_response: GetResponse,
1733        fetch_portal_name: Option<String>,
1734        timeout: ExecuteTimeout,
1735        outer_ctx_extra: Option<ExecuteContextGuard>,
1736        received: Option<EpochMillis>,
1737        ends_simple_query: bool,
1738    ) -> BoxFuture<'_, Result<State, io::Error>> {
1739        async move {
1740            let aborted_txn = self.is_aborted_txn();
1741
1742            // Check if the portal has been started and can be continued.
1743            let portal = match self
1744                .adapter_client
1745                .session()
1746                .get_portal_unverified_mut(&portal_name)
1747            {
1748                Some(portal) => portal,
1749                None => {
1750                    let msg = format!("portal {} does not exist", portal_name.quoted());
1751                    if let Some(outer_ctx_extra) = outer_ctx_extra {
1752                        self.adapter_client.retire_execute(
1753                            outer_ctx_extra,
1754                            StatementEndedExecutionReason::Errored { error: msg.clone() },
1755                        );
1756                    }
1757                    return self
1758                        .send_error_and_get_state(ErrorResponse::error(
1759                            SqlState::INVALID_CURSOR_NAME,
1760                            msg,
1761                        ))
1762                        .await;
1763                }
1764            };
1765
1766            *portal.lifecycle_timestamps = received.map(LifecycleTimestamps::new);
1767
1768            // In an aborted transaction, reject all commands except COMMIT/ROLLBACK.
1769            let txn_exit_stmt = is_txn_exit_stmt(portal.stmt.as_deref());
1770            if aborted_txn && !txn_exit_stmt {
1771                if let Some(outer_ctx_extra) = outer_ctx_extra {
1772                    self.adapter_client.retire_execute(
1773                        outer_ctx_extra,
1774                        StatementEndedExecutionReason::Errored {
1775                            error: ABORTED_TXN_MSG.to_string(),
1776                        },
1777                    );
1778                }
1779                return self.aborted_txn_error().await;
1780            }
1781
1782            let row_desc = portal.desc.relation_desc.clone();
1783            match portal.state {
1784                PortalState::NotStarted => {
1785                    // Start a transaction if we aren't in one.
1786                    self.ensure_transaction(1, "execute").await?;
1787                    match self
1788                        .adapter_client
1789                        .execute(
1790                            portal_name.clone(),
1791                            self.conn.wait_closed(),
1792                            outer_ctx_extra,
1793                        )
1794                        .await
1795                    {
1796                        Ok((response, execute_started)) => {
1797                            self.send_execute_response(
1798                                response,
1799                                row_desc,
1800                                portal_name,
1801                                max_rows,
1802                                get_response,
1803                                fetch_portal_name,
1804                                timeout,
1805                                execute_started,
1806                                ends_simple_query,
1807                            )
1808                            .await
1809                        }
1810                        Err(e) => {
1811                            self.send_pending_notices().await?;
1812                            self.send_error_and_get_state(e.into_response(Severity::Error))
1813                                .await
1814                        }
1815                    }
1816                }
1817                PortalState::InProgress(rows) => {
1818                    let rows = rows.take().expect("InProgress rows must be populated");
1819                    let (result, statement_ended_execution_reason) = match self
1820                        .send_rows(
1821                            row_desc.expect("portal missing row desc on resumption"),
1822                            portal_name,
1823                            rows,
1824                            max_rows,
1825                            get_response,
1826                            fetch_portal_name,
1827                            timeout,
1828                            ends_simple_query,
1829                        )
1830                        .await
1831                    {
1832                        Err(e) => {
1833                            // This is an error communicating with the connection.
1834                            // We consider that to be a cancelation, rather than a query error.
1835                            (Err(e), StatementEndedExecutionReason::Canceled)
1836                        }
1837                        Ok((ok, SendRowsEndedReason::Canceled)) => {
1838                            (Ok(ok), StatementEndedExecutionReason::Canceled)
1839                        }
1840                        // NOTE: For now the values for `result_size` and
1841                        // `rows_returned` in fetches are a bit confusing.
1842                        // We record `Some(n)` for the first fetch, where `n` is
1843                        // the number of bytes/rows returned by the inner
1844                        // execute (regardless of how many rows the
1845                        // fetch fetched), and `None` for subsequent fetches.
1846                        //
1847                        // This arguably makes sense since the size/rows
1848                        // returned measures how much work the compute
1849                        // layer had to do to satisfy the query, but
1850                        // we should revisit it if/when we start
1851                        // logging the inner execute separately.
1852                        Ok((
1853                            ok,
1854                            SendRowsEndedReason::Success {
1855                                result_size: _,
1856                                rows_returned: _,
1857                            },
1858                        )) => (
1859                            Ok(ok),
1860                            StatementEndedExecutionReason::Success {
1861                                result_size: None,
1862                                rows_returned: None,
1863                                execution_strategy: None,
1864                            },
1865                        ),
1866                        Ok((ok, SendRowsEndedReason::Errored { error })) => {
1867                            (Ok(ok), StatementEndedExecutionReason::Errored { error })
1868                        }
1869                    };
1870                    if let Some(outer_ctx_extra) = outer_ctx_extra {
1871                        self.adapter_client
1872                            .retire_execute(outer_ctx_extra, statement_ended_execution_reason);
1873                    }
1874                    result
1875                }
1876                // FETCH is an awkward command for our current architecture. In Postgres it
1877                // will extract <count> rows from the target portal, cache them, and return
1878                // them to the user as requested. Its command tag is always FETCH <num rows
1879                // extracted>. In Materialize, since we have chosen to not fully support FETCH,
1880                // we must remember the number of rows that were returned. Use this tag to
1881                // remember that information and return it.
1882                PortalState::Completed(Some(tag)) => {
1883                    let tag = tag.to_string();
1884                    if let Some(outer_ctx_extra) = outer_ctx_extra {
1885                        self.adapter_client.retire_execute(
1886                            outer_ctx_extra,
1887                            StatementEndedExecutionReason::Success {
1888                                result_size: None,
1889                                rows_returned: None,
1890                                execution_strategy: None,
1891                            },
1892                        );
1893                    }
1894                    if let Some(err) = self.settle_statement(ends_simple_query).await {
1895                        return self.send_error_and_get_state(err).await;
1896                    }
1897                    self.send(BackendMessage::CommandComplete { tag }).await?;
1898                    Ok(State::Ready)
1899                }
1900                PortalState::Completed(None) => {
1901                    let error = format!(
1902                        "portal {} cannot be run",
1903                        Ident::new_unchecked(portal_name).to_ast_string_stable()
1904                    );
1905                    if let Some(outer_ctx_extra) = outer_ctx_extra {
1906                        self.adapter_client.retire_execute(
1907                            outer_ctx_extra,
1908                            StatementEndedExecutionReason::Errored {
1909                                error: error.clone(),
1910                            },
1911                        );
1912                    }
1913                    self.send_error_and_get_state(ErrorResponse::error(
1914                        SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE,
1915                        error,
1916                    ))
1917                    .await
1918                }
1919            }
1920        }
1921        .instrument(debug_span!("execute"))
1922        .boxed()
1923    }
1924
1925    #[instrument(level = "debug")]
1926    async fn describe_statement(&mut self, name: &str) -> Result<State, io::Error> {
1927        // Start a transaction if we aren't in one.
1928        self.ensure_transaction(1, "describe_statement").await?;
1929
1930        let stmt = match self.adapter_client.get_prepared_statement(name).await {
1931            Ok(stmt) => stmt,
1932            Err(err) => {
1933                return self
1934                    .send_error_and_get_state(err.into_response(Severity::Error))
1935                    .await;
1936            }
1937        };
1938        // Cloning to avoid a mutable borrow issue because `send` also uses `adapter_client`
1939        let parameter_desc = BackendMessage::ParameterDescription(
1940            stmt.desc()
1941                .param_types
1942                .iter()
1943                .map(mz_pgrepr::Type::from)
1944                .collect(),
1945        );
1946        // Claim that all results will be output in text format, even
1947        // though the true result formats are not yet known. A bit
1948        // weird, but this is the behavior that PostgreSQL specifies.
1949        let formats = vec![Format::Text; stmt.desc().arity()];
1950        let row_desc = describe_rows(stmt.desc(), &formats);
1951        self.send_all([parameter_desc, row_desc]).await?;
1952        Ok(State::Ready)
1953    }
1954
1955    #[instrument(level = "debug")]
1956    async fn describe_portal(&mut self, name: &str) -> Result<State, io::Error> {
1957        // Start a transaction if we aren't in one.
1958        self.ensure_transaction(1, "describe_portal").await?;
1959
1960        let session = self.adapter_client.session();
1961        let row_desc = session
1962            .get_portal_unverified(name)
1963            .map(|portal| describe_rows(&portal.desc, &portal.result_formats));
1964        match row_desc {
1965            Some(row_desc) => {
1966                self.send(row_desc).await?;
1967                Ok(State::Ready)
1968            }
1969            None => {
1970                self.send_error_and_get_state(ErrorResponse::error(
1971                    SqlState::INVALID_CURSOR_NAME,
1972                    format!("portal {} does not exist", name.quoted()),
1973                ))
1974                .await
1975            }
1976        }
1977    }
1978
1979    #[instrument(level = "debug")]
1980    async fn close_statement(&mut self, name: String) -> Result<State, io::Error> {
1981        self.adapter_client
1982            .session()
1983            .remove_prepared_statement(&name);
1984        self.send(BackendMessage::CloseComplete).await?;
1985        Ok(State::Ready)
1986    }
1987
1988    #[instrument(level = "debug")]
1989    async fn close_portal(&mut self, name: String) -> Result<State, io::Error> {
1990        self.adapter_client.session().remove_portal(&name);
1991        self.send(BackendMessage::CloseComplete).await?;
1992        Ok(State::Ready)
1993    }
1994
1995    /// Completes the portal that carried a `CLOSE`.
1996    ///
1997    /// The `CLOSE` can name that very portal, and sequencing has then already
1998    /// removed it. Marking the state is bookkeeping for a portal nobody will
1999    /// read again, so its absence is expected rather than an error.
2000    fn complete_portal_after_close(&mut self, name: &str) {
2001        if let Some(portal) = self
2002            .adapter_client
2003            .session()
2004            .get_portal_unverified_mut(name)
2005        {
2006            *portal.state = PortalState::Completed(None);
2007        }
2008    }
2009
2010    /// Completes the portal that carried a `DECLARE`.
2011    ///
2012    /// A successful `DECLARE` only ever adds a portal, and adding one under a
2013    /// name already in use fails with `DuplicateCursor`, so the executing portal
2014    /// is still present here.
2015    fn complete_portal_after_declare(&mut self, name: &str) {
2016        match self
2017            .adapter_client
2018            .session()
2019            .get_portal_unverified_mut(name)
2020        {
2021            Some(portal) => *portal.state = PortalState::Completed(None),
2022            None => {
2023                mz_ore::soft_panic_or_log!("portal {} vanished across a DECLARE", name.quoted())
2024            }
2025        }
2026    }
2027
2028    async fn fetch(
2029        &mut self,
2030        name: String,
2031        count: Option<FetchDirection>,
2032        max_rows: ExecuteCount,
2033        fetch_portal_name: Option<String>,
2034        timeout: ExecuteTimeout,
2035        ctx_extra: ExecuteContextGuard,
2036        ends_simple_query: bool,
2037    ) -> Result<State, io::Error> {
2038        // Unlike Execute, no count specified in FETCH returns 1 row, and 0 means 0
2039        // instead of All.
2040        let count = count.unwrap_or(FetchDirection::ForwardCount(1));
2041
2042        // Figure out how many rows we should send back by looking at the various
2043        // combinations of the execute and fetch.
2044        //
2045        // In Postgres, Fetch will cache <count> rows from the target portal and
2046        // return those as requested (if, say, an Execute message was sent with a
2047        // max_rows < the Fetch's count). We expect that case to be incredibly rare and
2048        // so have chosen to not support it until users request it. This eases
2049        // implementation difficulty since we don't have to be able to "send" rows to
2050        // a buffer.
2051        //
2052        // TODO(mjibson): Test this somehow? Need to divide up the pgtest files in
2053        // order to have some that are not Postgres compatible.
2054        let count = match (max_rows, count) {
2055            (ExecuteCount::Count(max_rows), FetchDirection::ForwardCount(count)) => {
2056                let count = usize::cast_from(count);
2057                if max_rows < count {
2058                    let msg = "Execute with max_rows < a FETCH's count is not supported";
2059                    self.adapter_client.retire_execute(
2060                        ctx_extra,
2061                        StatementEndedExecutionReason::Errored {
2062                            error: msg.to_string(),
2063                        },
2064                    );
2065                    return self
2066                        .send_error_and_get_state(ErrorResponse::error(
2067                            SqlState::FEATURE_NOT_SUPPORTED,
2068                            msg,
2069                        ))
2070                        .await;
2071                }
2072                ExecuteCount::Count(count)
2073            }
2074            (ExecuteCount::Count(_), FetchDirection::ForwardAll) => {
2075                let msg = "Execute with max_rows of a FETCH ALL is not supported";
2076                self.adapter_client.retire_execute(
2077                    ctx_extra,
2078                    StatementEndedExecutionReason::Errored {
2079                        error: msg.to_string(),
2080                    },
2081                );
2082                return self
2083                    .send_error_and_get_state(ErrorResponse::error(
2084                        SqlState::FEATURE_NOT_SUPPORTED,
2085                        msg,
2086                    ))
2087                    .await;
2088            }
2089            (ExecuteCount::All, FetchDirection::ForwardAll) => ExecuteCount::All,
2090            (ExecuteCount::All, FetchDirection::ForwardCount(count)) => {
2091                ExecuteCount::Count(usize::cast_from(count))
2092            }
2093        };
2094        let cursor_name = name.to_string();
2095        self.execute(
2096            cursor_name,
2097            count,
2098            fetch_message,
2099            fetch_portal_name,
2100            timeout,
2101            Some(ctx_extra),
2102            None,
2103            ends_simple_query,
2104        )
2105        .await
2106    }
2107
2108    async fn flush(&mut self) -> Result<State, io::Error> {
2109        self.conn.flush().await?;
2110        Ok(State::Ready)
2111    }
2112
2113    /// Sends a backend message to the client, after applying a severity filter.
2114    ///
2115    /// The message is only sent if its severity is above the severity set
2116    /// in the session, with the default value being NOTICE.
2117    #[instrument(level = "debug")]
2118    async fn send<M>(&mut self, message: M) -> Result<(), io::Error>
2119    where
2120        M: Into<BackendMessage>,
2121    {
2122        let message: BackendMessage = message.into();
2123        let is_error =
2124            matches!(&message, BackendMessage::ErrorResponse(e) if e.severity.is_error());
2125
2126        self.conn.send(message).await?;
2127
2128        // Flush immediately after sending an error response, as some clients
2129        // expect to be able to read the error response before sending a Sync
2130        // message. This is arguably in violation of the protocol specification,
2131        // but the specification is somewhat ambiguous, and easier to match
2132        // PostgreSQL here than to fix all the clients that have this
2133        // expectation.
2134        if is_error {
2135            self.conn.flush().await?;
2136        }
2137
2138        Ok(())
2139    }
2140
2141    #[instrument(level = "debug")]
2142    pub async fn send_all(
2143        &mut self,
2144        messages: impl IntoIterator<Item = BackendMessage>,
2145    ) -> Result<(), io::Error> {
2146        for m in messages {
2147            self.send(m).await?;
2148        }
2149        Ok(())
2150    }
2151
2152    #[instrument(level = "debug")]
2153    async fn sync(&mut self) -> Result<State, io::Error> {
2154        // Close the current transaction if we are in an implicit transaction.
2155        if self.adapter_client.session().transaction().is_implicit() {
2156            self.commit_transaction().await?;
2157        }
2158        self.ready().await
2159    }
2160
2161    #[instrument(level = "debug")]
2162    async fn ready(&mut self) -> Result<State, io::Error> {
2163        let reverted_parameters = mem::take(&mut self.reverted_parameters);
2164        self.send_parameter_statuses(reverted_parameters).await?;
2165        let txn_state = self.adapter_client.session().transaction().into();
2166        self.send(BackendMessage::ReadyForQuery(txn_state)).await?;
2167        self.flush().await
2168    }
2169
2170    #[allow(clippy::too_many_arguments)]
2171    #[instrument(level = "debug")]
2172    async fn send_execute_response(
2173        &mut self,
2174        response: ExecuteResponse,
2175        row_desc: Option<RelationDesc>,
2176        portal_name: String,
2177        max_rows: ExecuteCount,
2178        get_response: GetResponse,
2179        fetch_portal_name: Option<String>,
2180        timeout: ExecuteTimeout,
2181        execute_started: Instant,
2182        ends_simple_query: bool,
2183    ) -> Result<State, io::Error> {
2184        let execution_time = self
2185            .adapter_client
2186            .session()
2187            .maybe_mark_staged(ExecutionTime::completed(execute_started.elapsed()));
2188        self.send_pending_notices().await?;
2189        let mut tag = response.tag();
2190
2191        macro_rules! command_complete {
2192            () => {{
2193                if let Some(err) = self.settle_statement(ends_simple_query).await {
2194                    return self.send_error_and_get_state(err).await;
2195                }
2196                self.send_execution_time(Some(execution_time)).await?;
2197                self.send(BackendMessage::CommandComplete {
2198                    tag: tag
2199                        .take()
2200                        .expect("command_complete only called on tag-generating results"),
2201                })
2202                .await?;
2203                Ok(State::Ready)
2204            }};
2205        }
2206
2207        let r = match response {
2208            ExecuteResponse::ClosedCursor => {
2209                self.complete_portal_after_close(&portal_name);
2210                command_complete!()
2211            }
2212            ExecuteResponse::DeclaredCursor => {
2213                self.complete_portal_after_declare(&portal_name);
2214                command_complete!()
2215            }
2216            ExecuteResponse::EmptyQuery => {
2217                self.send(BackendMessage::EmptyQueryResponse).await?;
2218                Ok(State::Ready)
2219            }
2220            ExecuteResponse::Fetch {
2221                name,
2222                count,
2223                timeout,
2224                ctx_extra,
2225            } => {
2226                self.fetch(
2227                    name,
2228                    count,
2229                    max_rows,
2230                    Some(portal_name.to_string()),
2231                    timeout,
2232                    ctx_extra,
2233                    ends_simple_query,
2234                )
2235                .await
2236            }
2237            ExecuteResponse::SendingRowsStreaming {
2238                rows,
2239                instance_id,
2240                strategy,
2241            } => {
2242                let row_desc = row_desc
2243                    .expect("missing row description for ExecuteResponse::SendingRowsStreaming");
2244
2245                let span = tracing::debug_span!("sending_rows_streaming");
2246
2247                self.send_rows(
2248                    row_desc,
2249                    portal_name,
2250                    InProgressRows::new(RecordFirstRowStream::new(
2251                        Box::new(rows),
2252                        execute_started,
2253                        &self.adapter_client,
2254                        Some(instance_id),
2255                        Some(strategy),
2256                    )),
2257                    max_rows,
2258                    get_response,
2259                    fetch_portal_name,
2260                    timeout,
2261                    ends_simple_query,
2262                )
2263                .instrument(span)
2264                .await
2265                .map(|(state, _)| state)
2266            }
2267            ExecuteResponse::SendingRowsImmediate { rows } => {
2268                let row_desc = row_desc
2269                    .expect("missing row description for ExecuteResponse::SendingRowsImmediate");
2270
2271                let span = tracing::debug_span!("sending_rows_immediate");
2272
2273                let stream =
2274                    futures::stream::once(futures::future::ready(PeekResponseUnary::Rows(rows)));
2275                self.send_rows(
2276                    row_desc,
2277                    portal_name,
2278                    InProgressRows::new(RecordFirstRowStream::new(
2279                        Box::new(stream),
2280                        execute_started,
2281                        &self.adapter_client,
2282                        None,
2283                        Some(StatementExecutionStrategy::Constant),
2284                    )),
2285                    max_rows,
2286                    get_response,
2287                    fetch_portal_name,
2288                    timeout,
2289                    ends_simple_query,
2290                )
2291                .instrument(span)
2292                .await
2293                .map(|(state, _)| state)
2294            }
2295            ExecuteResponse::SetVariable { name, .. } => {
2296                // This code is somewhat awkwardly structured because we
2297                // can't hold `var` across an await point.
2298                let qn = name.to_string();
2299                let msg = if let Some(var) = self
2300                    .adapter_client
2301                    .session()
2302                    .vars_mut()
2303                    .notify_set()
2304                    .find(|v| v.name() == qn)
2305                {
2306                    Some(BackendMessage::ParameterStatus(var.name(), var.value()))
2307                } else {
2308                    None
2309                };
2310                if let Some(msg) = msg {
2311                    self.send(msg).await?;
2312                }
2313                command_complete!()
2314            }
2315            ExecuteResponse::Subscribing {
2316                rx,
2317                ctx_extra,
2318                instance_id,
2319            } => {
2320                if fetch_portal_name.is_none() {
2321                    let mut msg = ErrorResponse::notice(
2322                        SqlState::WARNING,
2323                        "streaming SUBSCRIBE rows directly requires a client that does not buffer output",
2324                    );
2325                    if self.adapter_client.session().vars().application_name() == "psql" {
2326                        msg.hint = Some(
2327                            "Wrap your SUBSCRIBE statement in `COPY (SUBSCRIBE ...) TO STDOUT`."
2328                                .into(),
2329                        )
2330                    }
2331                    self.send(msg).await?;
2332                    self.conn.flush().await?;
2333                }
2334                let row_desc =
2335                    row_desc.expect("missing row description for ExecuteResponse::Subscribing");
2336                let (result, statement_ended_execution_reason) = match self
2337                    .send_rows(
2338                        row_desc,
2339                        portal_name,
2340                        InProgressRows::new(
2341                            RecordFirstRowStream::new(
2342                                rx,
2343                                execute_started,
2344                                &self.adapter_client,
2345                                Some(instance_id),
2346                                None,
2347                            )
2348                            .without_execution_time(),
2349                        ),
2350                        max_rows,
2351                        get_response,
2352                        fetch_portal_name,
2353                        timeout,
2354                        ends_simple_query,
2355                    )
2356                    .await
2357                {
2358                    Err(e) => {
2359                        // This is an error communicating with the connection.
2360                        // We consider that to be a cancelation, rather than a query error.
2361                        (Err(e), StatementEndedExecutionReason::Canceled)
2362                    }
2363                    Ok((ok, SendRowsEndedReason::Canceled)) => {
2364                        (Ok(ok), StatementEndedExecutionReason::Canceled)
2365                    }
2366                    Ok((
2367                        ok,
2368                        SendRowsEndedReason::Success {
2369                            result_size,
2370                            rows_returned,
2371                        },
2372                    )) => (
2373                        Ok(ok),
2374                        StatementEndedExecutionReason::Success {
2375                            result_size: Some(result_size),
2376                            rows_returned: Some(rows_returned),
2377                            execution_strategy: None,
2378                        },
2379                    ),
2380                    Ok((ok, SendRowsEndedReason::Errored { error })) => {
2381                        (Ok(ok), StatementEndedExecutionReason::Errored { error })
2382                    }
2383                };
2384                self.adapter_client
2385                    .retire_execute(ctx_extra, statement_ended_execution_reason);
2386                return result;
2387            }
2388            ExecuteResponse::CopyTo { format, resp } => {
2389                let row_desc =
2390                    row_desc.expect("missing row description for ExecuteResponse::CopyTo");
2391                match *resp {
2392                    ExecuteResponse::Subscribing {
2393                        rx,
2394                        ctx_extra,
2395                        instance_id,
2396                    } => {
2397                        let (result, statement_ended_execution_reason) = match self
2398                            .copy_rows(
2399                                format,
2400                                row_desc,
2401                                RecordFirstRowStream::new(
2402                                    rx,
2403                                    execute_started,
2404                                    &self.adapter_client,
2405                                    Some(instance_id),
2406                                    None,
2407                                ),
2408                                ends_simple_query,
2409                            )
2410                            .await
2411                        {
2412                            Err(e) => {
2413                                // This is an error communicating with the connection.
2414                                // We consider that to be a cancelation, rather than a query error.
2415                                (Err(e), StatementEndedExecutionReason::Canceled)
2416                            }
2417                            Ok((
2418                                state,
2419                                SendRowsEndedReason::Success {
2420                                    result_size,
2421                                    rows_returned,
2422                                },
2423                            )) => (
2424                                Ok(state),
2425                                StatementEndedExecutionReason::Success {
2426                                    result_size: Some(result_size),
2427                                    rows_returned: Some(rows_returned),
2428                                    execution_strategy: None,
2429                                },
2430                            ),
2431                            Ok((state, SendRowsEndedReason::Errored { error })) => {
2432                                (Ok(state), StatementEndedExecutionReason::Errored { error })
2433                            }
2434                            Ok((state, SendRowsEndedReason::Canceled)) => {
2435                                (Ok(state), StatementEndedExecutionReason::Canceled)
2436                            }
2437                        };
2438                        self.adapter_client
2439                            .retire_execute(ctx_extra, statement_ended_execution_reason);
2440                        return result;
2441                    }
2442                    ExecuteResponse::SendingRowsStreaming {
2443                        rows,
2444                        instance_id,
2445                        strategy,
2446                    } => {
2447                        // We don't need to finalize execution here;
2448                        // it was already done in the
2449                        // coordinator. Just extract the state and
2450                        // return that.
2451                        return self
2452                            .copy_rows(
2453                                format,
2454                                row_desc,
2455                                RecordFirstRowStream::new(
2456                                    Box::new(rows),
2457                                    execute_started,
2458                                    &self.adapter_client,
2459                                    Some(instance_id),
2460                                    Some(strategy),
2461                                ),
2462                                ends_simple_query,
2463                            )
2464                            .await
2465                            .map(|(state, _)| state);
2466                    }
2467                    ExecuteResponse::SendingRowsImmediate { rows } => {
2468                        let span = tracing::debug_span!("sending_rows_immediate");
2469
2470                        let rows = futures::stream::once(futures::future::ready(
2471                            PeekResponseUnary::Rows(rows),
2472                        ));
2473                        // We don't need to finalize execution here;
2474                        // it was already done in the
2475                        // coordinator. Just extract the state and
2476                        // return that.
2477                        return self
2478                            .copy_rows(
2479                                format,
2480                                row_desc,
2481                                RecordFirstRowStream::new(
2482                                    Box::new(rows),
2483                                    execute_started,
2484                                    &self.adapter_client,
2485                                    None,
2486                                    Some(StatementExecutionStrategy::Constant),
2487                                ),
2488                                ends_simple_query,
2489                            )
2490                            .instrument(span)
2491                            .await
2492                            .map(|(state, _)| state);
2493                    }
2494                    _ => {
2495                        return self
2496                            .send_error_and_get_state(ErrorResponse::error(
2497                                SqlState::INTERNAL_ERROR,
2498                                "unsupported COPY response type".to_string(),
2499                            ))
2500                            .await;
2501                    }
2502                };
2503            }
2504            ExecuteResponse::CopyFrom {
2505                target_id,
2506                target_name,
2507                columns,
2508                params,
2509                ctx_extra,
2510            } => {
2511                let row_desc =
2512                    row_desc.expect("missing row description for ExecuteResponse::CopyFrom");
2513                self.copy_from(
2514                    target_id,
2515                    target_name,
2516                    columns,
2517                    params,
2518                    row_desc,
2519                    ctx_extra,
2520                    ends_simple_query,
2521                )
2522                .await
2523            }
2524            ExecuteResponse::TransactionCommitted { params }
2525            | ExecuteResponse::TransactionRolledBack { params }
2526            | ExecuteResponse::DiscardedAll { params } => {
2527                self.send_parameter_statuses(params).await?;
2528                command_complete!()
2529            }
2530
2531            ExecuteResponse::AlteredDefaultPrivileges
2532            | ExecuteResponse::AlteredObject(..)
2533            | ExecuteResponse::AlteredRole
2534            | ExecuteResponse::AlteredSystemConfiguration
2535            | ExecuteResponse::CreatedCluster { .. }
2536            | ExecuteResponse::CreatedClusterReplica { .. }
2537            | ExecuteResponse::CreatedConnection { .. }
2538            | ExecuteResponse::CreatedDatabase { .. }
2539            | ExecuteResponse::CreatedIndex { .. }
2540            | ExecuteResponse::CreatedMetricSink { .. }
2541            | ExecuteResponse::CreatedIntrospectionSubscribe
2542            | ExecuteResponse::CreatedMaterializedView { .. }
2543            | ExecuteResponse::CreatedRole
2544            | ExecuteResponse::CreatedSchema { .. }
2545            | ExecuteResponse::CreatedSecret { .. }
2546            | ExecuteResponse::CreatedSink { .. }
2547            | ExecuteResponse::CreatedSource { .. }
2548            | ExecuteResponse::CreatedTable { .. }
2549            | ExecuteResponse::CreatedType
2550            | ExecuteResponse::CreatedView { .. }
2551            | ExecuteResponse::CreatedViews { .. }
2552            | ExecuteResponse::CreatedNetworkPolicy
2553            | ExecuteResponse::Comment
2554            | ExecuteResponse::Deallocate { .. }
2555            | ExecuteResponse::Deleted(..)
2556            | ExecuteResponse::DiscardedTemp
2557            | ExecuteResponse::DroppedObject(_)
2558            | ExecuteResponse::DroppedOwned
2559            | ExecuteResponse::GrantedPrivilege
2560            | ExecuteResponse::GrantedRole
2561            | ExecuteResponse::Inserted(..)
2562            | ExecuteResponse::Copied(..)
2563            | ExecuteResponse::Prepare
2564            | ExecuteResponse::Raised
2565            | ExecuteResponse::ReassignOwned
2566            | ExecuteResponse::RevokedPrivilege
2567            | ExecuteResponse::RevokedRole
2568            | ExecuteResponse::StartedTransaction { .. }
2569            | ExecuteResponse::Updated(..)
2570            | ExecuteResponse::ValidatedConnection => {
2571                command_complete!()
2572            }
2573        };
2574
2575        assert_none!(tag, "tag created but not consumed: {:?}", tag);
2576        r
2577    }
2578
2579    #[allow(clippy::too_many_arguments)]
2580    // TODO(guswynn): figure out how to get it to compile without skip_all
2581    #[mz_ore::instrument(level = "debug")]
2582    async fn send_rows(
2583        &mut self,
2584        row_desc: RelationDesc,
2585        portal_name: String,
2586        mut rows: InProgressRows,
2587        max_rows: ExecuteCount,
2588        get_response: GetResponse,
2589        fetch_portal_name: Option<String>,
2590        timeout: ExecuteTimeout,
2591        ends_simple_query: bool,
2592    ) -> Result<(State, SendRowsEndedReason), io::Error> {
2593        // If this portal is being executed from a FETCH then we need to use the result
2594        // format type of the outer portal.
2595        let result_format_portal_name: &str = if let Some(ref name) = fetch_portal_name {
2596            name
2597        } else {
2598            &portal_name
2599        };
2600        let result_formats = self
2601            .adapter_client
2602            .session()
2603            .get_portal_unverified(result_format_portal_name)
2604            .expect("valid fetch portal name for send rows")
2605            .result_formats
2606            .clone();
2607
2608        let (mut wait_once, mut deadline) = match timeout {
2609            ExecuteTimeout::None => (false, None),
2610            ExecuteTimeout::Seconds(t) => (
2611                false,
2612                Some(tokio::time::Instant::now() + tokio::time::Duration::from_secs_f64(t)),
2613            ),
2614            ExecuteTimeout::WaitOnce => (true, None),
2615        };
2616
2617        // Sanity check that the various `RelationDesc`s match up.
2618        {
2619            let portal_name_desc = &self
2620                .adapter_client
2621                .session()
2622                .get_portal_unverified(portal_name.as_str())
2623                .expect("portal should exist")
2624                .desc
2625                .relation_desc;
2626            if let Some(portal_name_desc) = portal_name_desc {
2627                soft_assert_eq_or_log!(portal_name_desc, &row_desc);
2628            }
2629            if let Some(fetch_portal_name) = &fetch_portal_name {
2630                let fetch_portal_desc = &self
2631                    .adapter_client
2632                    .session()
2633                    .get_portal_unverified(fetch_portal_name)
2634                    .expect("portal should exist")
2635                    .desc
2636                    .relation_desc;
2637                if let Some(fetch_portal_desc) = fetch_portal_desc {
2638                    soft_assert_eq_or_log!(fetch_portal_desc, &row_desc);
2639                }
2640            }
2641        }
2642
2643        self.conn.set_encode_state(
2644            row_desc
2645                .typ()
2646                .column_types
2647                .iter()
2648                .map(|ty| mz_pgrepr::Type::from(&ty.scalar_type))
2649                .zip_eq(result_formats)
2650                .collect(),
2651            self.adapter_client.session().vars().text_encode_settings(),
2652        );
2653
2654        let mut total_sent_rows = 0;
2655        let mut total_sent_bytes = 0;
2656        // want_rows is the maximum number of rows the client wants.
2657        let mut want_rows = match max_rows {
2658            ExecuteCount::All => usize::MAX,
2659            ExecuteCount::Count(count) => count,
2660        };
2661
2662        // Send rows while the client still wants them and there are still rows to send.
2663        loop {
2664            // Fetch next batch of rows, waiting for a possible requested
2665            // timeout or notice.
2666            let batch = if rows.current.is_some() {
2667                FetchResult::Rows(rows.current.take())
2668            } else if want_rows == 0 {
2669                FetchResult::Rows(None)
2670            } else {
2671                let notice_fut = self.adapter_client.session().recv_notice();
2672                // Biased: drain available data before checking the deadline.
2673                // This is critical for the WaitOnce case, where the deadline
2674                // is set to `Instant::now()` right after the first batch:
2675                // without `biased`, `recv()` and the already-expired deadline
2676                // race nondeterministically, so we might break the loop
2677                // before `no_more_rows` is set (or even before ready rows
2678                // are consumed). With an explicit `TIMEOUT`, missing a batch
2679                // right at the boundary is acceptable, but WaitOnce fires
2680                // immediately and the race is not.
2681                //
2682                // Trade-off: if `recv()` keeps returning Ready (unlikely in
2683                // practice—row processing + flush is slower than upstream
2684                // tick granularity), a `TIMEOUT` deadline could be delayed.
2685                // See database-issues#9470.
2686                tokio::select! {
2687                    biased;
2688                    err = self.conn.wait_closed() => return Err(err),
2689                    batch = rows.remaining.recv() => match batch {
2690                        None => FetchResult::Rows(None),
2691                        Some(PeekResponseUnary::Rows(rows)) => FetchResult::Rows(Some(rows)),
2692                        Some(PeekResponseUnary::Error(err)) => {
2693                            FetchResult::Error(err.into_response(Severity::Error))
2694                        }
2695                        Some(PeekResponseUnary::DependencyDropped(dep)) => {
2696                            FetchResult::Error(
2697                                dep.to_concurrent_dependency_drop()
2698                                    .into_response(Severity::Error),
2699                            )
2700                        }
2701                        Some(PeekResponseUnary::Canceled) => FetchResult::Canceled,
2702                    },
2703                    notice = notice_fut => {
2704                        FetchResult::Notice(notice)
2705                    }
2706                    _ = time::sleep_until(
2707                        deadline.unwrap_or_else(tokio::time::Instant::now),
2708                    ), if deadline.is_some() => FetchResult::Rows(None),
2709                }
2710            };
2711
2712            match batch {
2713                FetchResult::Rows(None) => break,
2714                FetchResult::Rows(Some(mut batch_rows)) => {
2715                    if let Err(err) = verify_datum_desc(&row_desc, &mut batch_rows) {
2716                        let msg = err.to_string();
2717                        return self
2718                            .send_error_and_get_state(err.into_response(Severity::Error))
2719                            .await
2720                            .map(|state| (state, SendRowsEndedReason::Errored { error: msg }));
2721                    }
2722
2723                    // If wait_once is true: the first time this fn is called it blocks (same as
2724                    // deadline == None). The second time this fn is called it should behave the
2725                    // same a 0s timeout.
2726                    if wait_once && batch_rows.peek().is_some() {
2727                        deadline = Some(tokio::time::Instant::now());
2728                        wait_once = false;
2729                    }
2730
2731                    // Send a portion of the rows.
2732                    let mut sent_rows = 0;
2733                    let mut sent_bytes = 0;
2734                    let messages = (&mut batch_rows)
2735                        // TODO(parkmycar): This is a fair bit of juggling between iterator types
2736                        // to count the total number of bytes. Alternatively we could track the
2737                        // total sent bytes in this .map(...) call, but having side effects in map
2738                        // is a code smell.
2739                        .map(|row| {
2740                            let row_len = row.byte_len();
2741                            let values = mz_pgrepr::values_from_row(row, row_desc.typ());
2742                            (row_len, BackendMessage::DataRow(values))
2743                        })
2744                        .inspect(|(row_len, _)| {
2745                            sent_bytes += row_len;
2746                            sent_rows += 1
2747                        })
2748                        .map(|(_row_len, row)| row)
2749                        .take(want_rows);
2750                    self.send_all(messages).await?;
2751
2752                    total_sent_rows += sent_rows;
2753                    total_sent_bytes += sent_bytes;
2754                    want_rows -= sent_rows;
2755
2756                    // If we have sent the number of requested rows, put the remainder of the batch
2757                    // (if any) back and stop sending.
2758                    if want_rows == 0 {
2759                        if batch_rows.peek().is_some() {
2760                            rows.current = Some(batch_rows);
2761                        }
2762                        break;
2763                    }
2764
2765                    self.conn.flush().await?;
2766                }
2767                FetchResult::Notice(notice) => {
2768                    self.send(notice.into_response()).await?;
2769                    self.conn.flush().await?;
2770                }
2771                FetchResult::Error(err) => {
2772                    let text = err.message.clone();
2773                    return self
2774                        .send_error_and_get_state(err)
2775                        .await
2776                        .map(|state| (state, SendRowsEndedReason::Errored { error: text }));
2777                }
2778                FetchResult::Canceled => {
2779                    return self
2780                        .send_error_and_get_state(ErrorResponse::error(
2781                            SqlState::QUERY_CANCELED,
2782                            "canceling statement due to user request",
2783                        ))
2784                        .await
2785                        .map(|state| (state, SendRowsEndedReason::Canceled));
2786                }
2787            }
2788        }
2789
2790        let execution_time = rows
2791            .remaining
2792            .take_execution_time()
2793            .map(|time| self.adapter_client.session().maybe_mark_staged(time));
2794        let portal = self
2795            .adapter_client
2796            .session()
2797            .get_portal_unverified_mut(&portal_name)
2798            .expect("valid portal name for send rows");
2799
2800        let saw_rows = rows.remaining.saw_rows;
2801        let no_more_rows = rows.no_more_rows();
2802        let metric_recorded = rows.remaining.metric_recorded;
2803        let recorded_first_row_instant = rows.remaining.recorded_first_row_instant;
2804
2805        if no_more_rows && !metric_recorded {
2806            rows.remaining.metric_recorded = true;
2807        }
2808
2809        // Always return rows back, even if it's empty. This prevents an unclosed
2810        // portal from re-executing after it has been emptied.
2811        *portal.state = PortalState::InProgress(Some(rows));
2812
2813        let fetch_portal = fetch_portal_name.map(|name| {
2814            self.adapter_client
2815                .session()
2816                .get_portal_unverified_mut(&name)
2817                .expect("valid fetch portal")
2818        });
2819        let response_message = get_response(max_rows, total_sent_rows, fetch_portal);
2820        // Attend to metrics if there are no more rows. Only record once per stream
2821        // to avoid polluting the histogram when an exhausted cursor is FETCHed again.
2822        if no_more_rows && !metric_recorded {
2823            let statement_type = if let Some(stmt) = &self
2824                .adapter_client
2825                .session()
2826                .get_portal_unverified(&portal_name)
2827                .expect("valid portal name for send_rows")
2828                .stmt
2829            {
2830                metrics::statement_type_label_value(stmt.deref())
2831            } else {
2832                "no-statement"
2833            };
2834            let duration = if saw_rows {
2835                recorded_first_row_instant
2836                    .expect("recorded_first_row_instant because saw_rows")
2837                    .elapsed()
2838            } else {
2839                // If the result is empty, then we define time from first to last row as 0.
2840                // (Note that, currently, an empty result involves a PeekResponse with 0 rows, which
2841                // does flip `saw_rows`, so this code path is currently not exercised.)
2842                Duration::ZERO
2843            };
2844            self.adapter_client
2845                .inner()
2846                .metrics()
2847                .result_rows_first_to_last_byte_seconds
2848                .with_label_values(&[statement_type])
2849                .observe(duration.as_secs_f64());
2850        }
2851
2852        // Committing clears the session's portals, so this comes after their last use above.
2853        let state = match self.settle_statement(ends_simple_query).await {
2854            Some(err) => self.send_error_and_get_state(err).await?,
2855            None => {
2856                self.send_execution_time(execution_time).await?;
2857                self.send(response_message).await?;
2858                State::Ready
2859            }
2860        };
2861
2862        Ok((
2863            state,
2864            SendRowsEndedReason::Success {
2865                result_size: u64::cast_from(total_sent_bytes),
2866                rows_returned: u64::cast_from(total_sent_rows),
2867            },
2868        ))
2869    }
2870
2871    #[mz_ore::instrument(level = "debug")]
2872    async fn copy_rows(
2873        &mut self,
2874        format: CopyFormat,
2875        row_desc: RelationDesc,
2876        mut stream: RecordFirstRowStream,
2877        ends_simple_query: bool,
2878    ) -> Result<(State, SendRowsEndedReason), io::Error> {
2879        let (row_format, encode_format) = match format {
2880            CopyFormat::Text => (
2881                CopyFormatParams::Text(CopyTextFormatParams::default()),
2882                Format::Text,
2883            ),
2884            CopyFormat::Binary => (CopyFormatParams::Binary, Format::Binary),
2885            CopyFormat::Csv => (
2886                CopyFormatParams::Csv(CopyCsvFormatParams::default()),
2887                Format::Text,
2888            ),
2889            CopyFormat::Parquet => {
2890                let text = "Parquet format is not supported".to_string();
2891                return self
2892                    .send_error_and_get_state(ErrorResponse::error(
2893                        SqlState::INTERNAL_ERROR,
2894                        text.clone(),
2895                    ))
2896                    .await
2897                    .map(|state| (state, SendRowsEndedReason::Errored { error: text }));
2898            }
2899        };
2900
2901        // Binary encoding is not implemented for some types (e.g., list, map,
2902        // and aclitem). Unlike the extended query protocol's Bind handler, COPY
2903        // does not validate this when binding the portal: the portal's result
2904        // formats describe the `CopyData` wrapper, not the COPY format itself,
2905        // so the Bind handler explicitly skips `COPY TO` statements. We must
2906        // therefore check here, before streaming any rows, otherwise
2907        // `encode_binary` would panic mid-stream (SQL-323).
2908        if let CopyFormat::Binary = format {
2909            if let Some(msg) = row_desc
2910                .iter_types()
2911                .find_map(|ty| mz_pgrepr::Value::binary_encoding_error(&ty.scalar_type).err())
2912            {
2913                return self
2914                    .send_error_and_get_state(ErrorResponse::error(
2915                        SqlState::UNDEFINED_FUNCTION,
2916                        msg,
2917                    ))
2918                    .await
2919                    .map(|state| {
2920                        (
2921                            state,
2922                            SendRowsEndedReason::Errored {
2923                                error: msg.to_string(),
2924                            },
2925                        )
2926                    });
2927            }
2928        }
2929
2930        // Unlike `COPY TO <external destination>`, which is encoded in the
2931        // dataflow layer, `COPY TO STDOUT` runs in the session and so honors
2932        // the session's encoding settings, matching PostgreSQL.
2933        let text_settings = self.adapter_client.session().vars().text_encode_settings();
2934        let encode_fn = |row: &RowRef, typ: &SqlRelationType, out: &mut Vec<u8>| {
2935            mz_pgcopy::encode_copy_format(&row_format, row, typ, out, text_settings)
2936        };
2937
2938        let typ = row_desc.typ();
2939        let column_formats = iter::repeat(encode_format)
2940            .take(typ.column_types.len())
2941            .collect();
2942        self.send(BackendMessage::CopyOutResponse {
2943            overall_format: encode_format,
2944            column_formats,
2945        })
2946        .await?;
2947
2948        // In Postgres, binary copy has a header that is followed (in the same
2949        // CopyData) by the first row. In order to replicate their behavior, use a
2950        // common vec that we can extend one time now and then fill up with the encode
2951        // functions.
2952        let mut out = Vec::new();
2953
2954        if let CopyFormat::Binary = format {
2955            // 11-byte signature.
2956            out.extend(b"PGCOPY\n\xFF\r\n\0");
2957            // 32-bit flags field.
2958            out.extend([0, 0, 0, 0]);
2959            // 32-bit header extension length field.
2960            out.extend([0, 0, 0, 0]);
2961        }
2962
2963        let mut count = 0;
2964        let mut total_sent_bytes = 0;
2965        loop {
2966            tokio::select! {
2967                e = self.conn.wait_closed() => return Err(e),
2968                batch = stream.recv() => match batch {
2969                    None => break,
2970                    Some(PeekResponseUnary::Error(err)) => {
2971                        let text = err.to_string();
2972                        return self
2973                            .send_error_and_get_state(err.into_response(Severity::Error))
2974                            .await
2975                            .map(|state| (state, SendRowsEndedReason::Errored { error: text }));
2976                    }
2977                    Some(PeekResponseUnary::DependencyDropped(dep)) => {
2978                        let err = dep.to_concurrent_dependency_drop();
2979                        let text = err.to_string();
2980                        let resp = err.into_response(Severity::Error);
2981                        return self
2982                            .send_error_and_get_state(resp)
2983                            .await
2984                            .map(|state| (state, SendRowsEndedReason::Errored { error: text }));
2985                    }
2986                    Some(PeekResponseUnary::Canceled) => {
2987                        return self.send_error_and_get_state(ErrorResponse::error(
2988                                SqlState::QUERY_CANCELED,
2989                                "canceling statement due to user request",
2990                            ))
2991                            .await.map(|state| (state, SendRowsEndedReason::Canceled));
2992                    }
2993                    Some(PeekResponseUnary::Rows(mut rows)) => {
2994                        count += rows.count();
2995                        while let Some(row) = rows.next() {
2996                            total_sent_bytes += row.byte_len();
2997                            encode_fn(row, typ, &mut out)?;
2998                            self.send(BackendMessage::CopyData(mem::take(&mut out)))
2999                                .await?;
3000                        }
3001                    }
3002                },
3003                notice = self.adapter_client.session().recv_notice() => {
3004                    self.send(notice.into_response())
3005                        .await?;
3006                    self.conn.flush().await?;
3007                }
3008            }
3009
3010            self.conn.flush().await?;
3011        }
3012        // Send required trailers.
3013        if let CopyFormat::Binary = format {
3014            let trailer: i16 = -1;
3015            out.extend(trailer.to_be_bytes());
3016            self.send(BackendMessage::CopyData(mem::take(&mut out)))
3017                .await?;
3018        }
3019
3020        let tag = format!("COPY {}", count);
3021        self.send(BackendMessage::CopyDone).await?;
3022        let state = match self.settle_statement(ends_simple_query).await {
3023            Some(err) => self.send_error_and_get_state(err).await?,
3024            None => {
3025                self.send(BackendMessage::CommandComplete { tag }).await?;
3026                State::Ready
3027            }
3028        };
3029        Ok((
3030            state,
3031            SendRowsEndedReason::Success {
3032                result_size: u64::cast_from(total_sent_bytes),
3033                rows_returned: u64::cast_from(count),
3034            },
3035        ))
3036    }
3037
3038    /// Handles the copy-in mode of the postgres protocol from transferring
3039    /// data to the server.
3040    #[instrument(level = "debug")]
3041    async fn copy_from(
3042        &mut self,
3043        target_id: CatalogItemId,
3044        target_name: String,
3045        columns: Vec<ColumnIndex>,
3046        params: CopyFormatParams<'static>,
3047        row_desc: RelationDesc,
3048        mut ctx_extra: ExecuteContextGuard,
3049        ends_simple_query: bool,
3050    ) -> Result<State, io::Error> {
3051        let res = self
3052            .copy_from_inner(
3053                target_id,
3054                target_name,
3055                columns,
3056                params,
3057                row_desc,
3058                &mut ctx_extra,
3059                ends_simple_query,
3060            )
3061            .await;
3062        match &res {
3063            Ok(State::Ready) => {
3064                self.adapter_client.retire_execute(
3065                    ctx_extra,
3066                    StatementEndedExecutionReason::Success {
3067                        result_size: None,
3068                        rows_returned: None,
3069                        execution_strategy: None,
3070                    },
3071                );
3072            }
3073            Ok(State::Done) => {
3074                // The connection closed gracefully without sending us a `CopyDone`,
3075                // causing us to just drop the copy request.
3076                // For the purposes of statement logging, we count this as a cancellation.
3077                self.adapter_client
3078                    .retire_execute(ctx_extra, StatementEndedExecutionReason::Canceled);
3079            }
3080            Err(e) => {
3081                self.adapter_client.retire_execute(
3082                    ctx_extra,
3083                    StatementEndedExecutionReason::Errored {
3084                        error: format!("{e}"),
3085                    },
3086                );
3087            }
3088            Ok(State::Drain) => {}
3089        }
3090        res
3091    }
3092
3093    async fn copy_from_inner(
3094        &mut self,
3095        target_id: CatalogItemId,
3096        target_name: String,
3097        columns: Vec<ColumnIndex>,
3098        params: CopyFormatParams<'static>,
3099        row_desc: RelationDesc,
3100        ctx_extra: &mut ExecuteContextGuard,
3101        ends_simple_query: bool,
3102    ) -> Result<State, io::Error> {
3103        let typ = row_desc.typ();
3104        let column_formats = vec![Format::Text; typ.column_types.len()];
3105        self.send(BackendMessage::CopyInResponse {
3106            overall_format: Format::Text,
3107            column_formats,
3108        })
3109        .await?;
3110        self.conn.flush().await?;
3111
3112        // Set up the parallel streaming batch builders in the coordinator.
3113        let writer = match self
3114            .adapter_client
3115            .start_copy_from_stdin(
3116                target_id,
3117                target_name.clone(),
3118                columns.clone(),
3119                row_desc.clone(),
3120                params.clone(),
3121            )
3122            .await
3123        {
3124            Ok(writer) => writer,
3125            Err(e) => {
3126                // Drain remaining CopyData/CopyDone/CopyFail messages from the
3127                // socket. Since CopyInResponse was already sent, the client may
3128                // have pipelined copy data that we must consume before returning
3129                // the error, otherwise they'd be misinterpreted as top-level
3130                // protocol messages and cause a deadlock.
3131                loop {
3132                    match self.conn.recv().await? {
3133                        Some(FrontendMessage::CopyData(_)) => {}
3134                        Some(FrontendMessage::CopyDone) | Some(FrontendMessage::CopyFail(_)) => {
3135                            break;
3136                        }
3137                        Some(FrontendMessage::Flush) | Some(FrontendMessage::Sync) => {}
3138                        Some(_) => break,
3139                        None => return Ok(State::Done),
3140                    }
3141                }
3142                self.adapter_client.retire_execute(
3143                    std::mem::take(ctx_extra),
3144                    StatementEndedExecutionReason::Errored {
3145                        error: e.to_string(),
3146                    },
3147                );
3148                return self
3149                    .send_error_and_get_state(e.into_response(Severity::Error))
3150                    .await;
3151            }
3152        };
3153
3154        // Batch size for splitting raw data across parallel workers (~32MB).
3155        const BATCH_SIZE: usize = 32 * 1024 * 1024;
3156        let max_copy_from_row_size = self
3157            .adapter_client
3158            .get_system_vars()
3159            .await
3160            .max_copy_from_row_size()
3161            .try_into()
3162            .unwrap_or(usize::MAX);
3163
3164        let mut data = Vec::new();
3165        let mut row_scanner = CopyRowScanner::new(&params);
3166        let num_workers = writer.batch_txs.len();
3167        let mut next_worker: usize = 0;
3168        let mut saw_copy_done = false;
3169        let mut saw_end_marker = false;
3170        let mut copy_from_error: Option<(SqlState, String)> = None;
3171
3172        // Receive loop: accumulate CopyData, split at row boundaries,
3173        // round-robin raw chunks to parallel batch builder workers.
3174        loop {
3175            let message = self.conn.recv().await?;
3176            match message {
3177                Some(FrontendMessage::CopyData(buf)) => {
3178                    if saw_end_marker {
3179                        // Per PostgreSQL COPY behavior, ignore all bytes after
3180                        // the end-of-copy marker until CopyDone.
3181                        continue;
3182                    }
3183                    data.extend(buf);
3184                    row_scanner.scan_new_bytes(&data);
3185
3186                    if let Some(end_pos) = row_scanner.end_marker_end() {
3187                        data.truncate(end_pos);
3188                        row_scanner.on_truncate(end_pos);
3189                        saw_end_marker = true;
3190                    }
3191
3192                    // Guard against pathological single rows that never terminate.
3193                    if row_scanner.current_row_size(data.len()) > max_copy_from_row_size {
3194                        copy_from_error = Some((
3195                            SqlState::INSUFFICIENT_RESOURCES,
3196                            format!(
3197                                "COPY FROM STDIN row exceeded max_copy_from_row_size \
3198                                 ({max_copy_from_row_size} bytes)"
3199                            ),
3200                        ));
3201                        break;
3202                    }
3203
3204                    // When buffer exceeds batch size, split at the last complete row
3205                    // and send the complete rows chunk to the next worker.
3206                    let mut send_failed = false;
3207                    while data.len() >= BATCH_SIZE {
3208                        let split_pos = match row_scanner.last_row_end() {
3209                            Some(pos) => pos,
3210                            None => break, // no complete row yet
3211                        };
3212                        let remainder = data.split_off(split_pos);
3213                        let chunk = std::mem::replace(&mut data, remainder);
3214                        row_scanner.on_split(split_pos);
3215                        if writer.batch_txs[next_worker].send(chunk).await.is_err() {
3216                            send_failed = true;
3217                            break;
3218                        }
3219                        next_worker = (next_worker + 1) % num_workers;
3220                    }
3221                    // Worker dropped (likely errored) — stop sending,
3222                    // fall through to completion_rx for the real error.
3223                    if send_failed {
3224                        break;
3225                    }
3226                }
3227                Some(FrontendMessage::CopyDone) => {
3228                    // Send any remaining data to the next worker.
3229                    if !data.is_empty() {
3230                        let chunk = std::mem::take(&mut data);
3231                        // Ignore send failure — completion_rx will have the error.
3232                        let _ = writer.batch_txs[next_worker].send(chunk).await;
3233                    }
3234                    saw_copy_done = true;
3235                    break;
3236                }
3237                Some(FrontendMessage::CopyFail(err)) => {
3238                    self.adapter_client.retire_execute(
3239                        std::mem::take(ctx_extra),
3240                        StatementEndedExecutionReason::Canceled,
3241                    );
3242                    // Drop the writer to signal cancellation to the background tasks.
3243                    drop(writer);
3244                    return self
3245                        .send_error_and_get_state(ErrorResponse::error(
3246                            SqlState::QUERY_CANCELED,
3247                            format!("COPY from stdin failed: {}", err),
3248                        ))
3249                        .await;
3250                }
3251                Some(FrontendMessage::Flush) | Some(FrontendMessage::Sync) => {}
3252                Some(_) => {
3253                    let msg = "unexpected message type during COPY from stdin";
3254                    self.adapter_client.retire_execute(
3255                        std::mem::take(ctx_extra),
3256                        StatementEndedExecutionReason::Errored {
3257                            error: msg.to_string(),
3258                        },
3259                    );
3260                    drop(writer);
3261                    return self
3262                        .send_error_and_get_state(ErrorResponse::error(
3263                            SqlState::PROTOCOL_VIOLATION,
3264                            msg,
3265                        ))
3266                        .await;
3267                }
3268                None => {
3269                    drop(writer);
3270                    return Ok(State::Done);
3271                }
3272            }
3273        }
3274
3275        // If we exited the receive loop before seeing `CopyDone` (e.g. because
3276        // a worker failed and dropped its channel), keep draining COPY input to
3277        // avoid desynchronizing the protocol state machine.
3278        if !saw_copy_done {
3279            loop {
3280                match self.conn.recv().await? {
3281                    Some(FrontendMessage::CopyData(_)) => {}
3282                    Some(FrontendMessage::CopyDone) | Some(FrontendMessage::CopyFail(_)) => {
3283                        break;
3284                    }
3285                    Some(FrontendMessage::Flush) | Some(FrontendMessage::Sync) => {}
3286                    Some(_) => {
3287                        let msg = "unexpected message type during COPY from stdin";
3288                        self.adapter_client.retire_execute(
3289                            std::mem::take(ctx_extra),
3290                            StatementEndedExecutionReason::Errored {
3291                                error: msg.to_string(),
3292                            },
3293                        );
3294                        drop(writer);
3295                        return self
3296                            .send_error_and_get_state(ErrorResponse::error(
3297                                SqlState::PROTOCOL_VIOLATION,
3298                                msg,
3299                            ))
3300                            .await;
3301                    }
3302                    None => {
3303                        drop(writer);
3304                        return Ok(State::Done);
3305                    }
3306                }
3307            }
3308        }
3309
3310        if let Some((code, msg)) = copy_from_error {
3311            self.adapter_client.retire_execute(
3312                std::mem::take(ctx_extra),
3313                StatementEndedExecutionReason::Errored { error: msg.clone() },
3314            );
3315            drop(writer);
3316            return self
3317                .send_error_and_get_state(ErrorResponse::error(code, msg))
3318                .await;
3319        }
3320
3321        // Drop all senders to signal EOF to the background batch builders.
3322        // If copy_err is set, a worker already failed — dropping the senders
3323        // will cause remaining workers to stop, and we'll get the real error
3324        // from completion_rx below.
3325        drop(writer.batch_txs);
3326
3327        // Wait for all parallel workers to finish building batches.
3328        let (proto_batches, row_count) = match writer.completion_rx.await {
3329            Ok(Ok(result)) => result,
3330            Ok(Err(e)) => {
3331                self.adapter_client.retire_execute(
3332                    std::mem::take(ctx_extra),
3333                    StatementEndedExecutionReason::Errored {
3334                        error: e.to_string(),
3335                    },
3336                );
3337                return self
3338                    .send_error_and_get_state(e.into_response(Severity::Error))
3339                    .await;
3340            }
3341            Err(_) => {
3342                let msg = "COPY FROM STDIN: background batch builder tasks dropped";
3343                self.adapter_client.retire_execute(
3344                    std::mem::take(ctx_extra),
3345                    StatementEndedExecutionReason::Errored {
3346                        error: msg.to_string(),
3347                    },
3348                );
3349                return self
3350                    .send_error_and_get_state(ErrorResponse::error(SqlState::INTERNAL_ERROR, msg))
3351                    .await;
3352            }
3353        };
3354
3355        // Stage all batches in the session's transaction for atomic commit.
3356        if let Err(e) = self
3357            .adapter_client
3358            .stage_copy_from_stdin_batches(target_id, proto_batches)
3359        {
3360            self.adapter_client.retire_execute(
3361                std::mem::take(ctx_extra),
3362                StatementEndedExecutionReason::Errored {
3363                    error: e.to_string(),
3364                },
3365            );
3366            return self
3367                .send_error_and_get_state(e.into_response(Severity::Error))
3368                .await;
3369        }
3370
3371        if let Some(err) = self.settle_statement(ends_simple_query).await {
3372            self.adapter_client.retire_execute(
3373                std::mem::take(ctx_extra),
3374                StatementEndedExecutionReason::Errored {
3375                    error: err.message.clone(),
3376                },
3377            );
3378            return self.send_error_and_get_state(err).await;
3379        }
3380        let tag = format!("COPY {}", row_count);
3381        self.send(BackendMessage::CommandComplete { tag }).await?;
3382
3383        Ok(State::Ready)
3384    }
3385
3386    #[instrument(level = "debug")]
3387    async fn send_pending_notices(&mut self) -> Result<(), io::Error> {
3388        let notices = self
3389            .adapter_client
3390            .session()
3391            .drain_notices()
3392            .into_iter()
3393            .map(|notice| BackendMessage::ErrorResponse(notice.into_response()));
3394        self.send_all(notices).await?;
3395        Ok(())
3396    }
3397
3398    #[instrument(level = "debug")]
3399    async fn send_error_and_get_state(&mut self, err: ErrorResponse) -> Result<State, io::Error> {
3400        assert!(err.severity.is_error());
3401        debug!(
3402            "cid={} error code={}",
3403            self.adapter_client.session().conn_id(),
3404            err.code.code()
3405        );
3406        let is_fatal = err.severity.is_fatal();
3407        self.send(BackendMessage::ErrorResponse(err)).await?;
3408
3409        let txn = self.adapter_client.session().transaction();
3410        match txn {
3411            // Error can be called from describe and parse and so might not be in an active
3412            // transaction.
3413            TransactionStatus::Default | TransactionStatus::Failed(_) => {}
3414            // In Started (i.e., a single statement), cleanup ourselves.
3415            TransactionStatus::Started(_) => {
3416                self.rollback_transaction().await?;
3417            }
3418            // Implicit transactions also clear themselves.
3419            TransactionStatus::InTransactionImplicit(_) => {
3420                self.rollback_transaction().await?;
3421            }
3422            // Explicit transactions move to failed.
3423            TransactionStatus::InTransaction(_) => {
3424                self.adapter_client.fail_transaction();
3425            }
3426        };
3427        if is_fatal {
3428            Ok(State::Done)
3429        } else {
3430            Ok(State::Drain)
3431        }
3432    }
3433
3434    #[instrument(level = "debug")]
3435    async fn aborted_txn_error(&mut self) -> Result<State, io::Error> {
3436        self.send(BackendMessage::ErrorResponse(ErrorResponse::error(
3437            SqlState::IN_FAILED_SQL_TRANSACTION,
3438            ABORTED_TXN_MSG,
3439        )))
3440        .await?;
3441        Ok(State::Drain)
3442    }
3443
3444    fn is_aborted_txn(&mut self) -> bool {
3445        matches!(
3446            self.adapter_client.session().transaction(),
3447            TransactionStatus::Failed(_)
3448        )
3449    }
3450}
3451
3452fn pad_formats(formats: Vec<Format>, n: usize) -> Result<Vec<Format>, String> {
3453    match (formats.len(), n) {
3454        (0, e) => Ok(vec![Format::Text; e]),
3455        (1, e) => Ok(iter::repeat(formats[0]).take(e).collect()),
3456        (a, e) if a == e => Ok(formats),
3457        (a, e) => Err(format!(
3458            "expected {} field format specifiers, but got {}",
3459            e, a
3460        )),
3461    }
3462}
3463
3464fn describe_rows(stmt_desc: &StatementDesc, formats: &[Format]) -> BackendMessage {
3465    match &stmt_desc.relation_desc {
3466        Some(desc) if !stmt_desc.is_copy => {
3467            BackendMessage::RowDescription(message::encode_row_description(desc, formats))
3468        }
3469        _ => BackendMessage::NoData,
3470    }
3471}
3472
3473type GetResponse = fn(
3474    max_rows: ExecuteCount,
3475    total_sent_rows: usize,
3476    fetch_portal: Option<PortalRefMut>,
3477) -> BackendMessage;
3478
3479// A GetResponse used by send_rows during execute messages on portals or for
3480// simple query messages.
3481fn portal_exec_message(
3482    max_rows: ExecuteCount,
3483    total_sent_rows: usize,
3484    _fetch_portal: Option<PortalRefMut>,
3485) -> BackendMessage {
3486    // If max_rows is not specified, we will always send back a CommandComplete. If
3487    // max_rows is specified, we only send CommandComplete if there were more rows
3488    // requested than were remaining. That is, if max_rows == number of rows that
3489    // were remaining before sending (not that are remaining after sending), then
3490    // we still send a PortalSuspended. The number of remaining rows after the rows
3491    // have been sent doesn't matter. This matches postgres.
3492    match max_rows {
3493        ExecuteCount::Count(max_rows) if max_rows <= total_sent_rows => {
3494            BackendMessage::PortalSuspended
3495        }
3496        _ => BackendMessage::CommandComplete {
3497            tag: format!("SELECT {}", total_sent_rows),
3498        },
3499    }
3500}
3501
3502// A GetResponse used by send_rows during FETCH queries.
3503fn fetch_message(
3504    _max_rows: ExecuteCount,
3505    total_sent_rows: usize,
3506    fetch_portal: Option<PortalRefMut>,
3507) -> BackendMessage {
3508    let tag = format!("FETCH {}", total_sent_rows);
3509    if let Some(portal) = fetch_portal {
3510        *portal.state = PortalState::Completed(Some(tag.clone()));
3511    }
3512    BackendMessage::CommandComplete { tag }
3513}
3514
3515fn get_authenticator(
3516    authenticator_kind: listeners::AuthenticatorKind,
3517    frontegg: Option<FronteggAuthenticator>,
3518    oidc: GenericOidcAuthenticator,
3519    adapter_client: mz_adapter::Client,
3520) -> Authenticator {
3521    match authenticator_kind {
3522        listeners::AuthenticatorKind::Frontegg => Authenticator::Frontegg(frontegg.expect(
3523            "Frontegg authenticator should exist with listeners::AuthenticatorKind::Frontegg",
3524        )),
3525        listeners::AuthenticatorKind::Password => Authenticator::Password(adapter_client),
3526        listeners::AuthenticatorKind::Sasl => Authenticator::Sasl(adapter_client),
3527        listeners::AuthenticatorKind::Oidc => Authenticator::Oidc(oidc),
3528        listeners::AuthenticatorKind::None => Authenticator::None,
3529    }
3530}
3531
3532#[derive(Debug, Copy, Clone)]
3533enum ExecuteCount {
3534    All,
3535    Count(usize),
3536}
3537
3538// See postgres' backend/tcop/postgres.c IsTransactionExitStmt.
3539fn is_txn_exit_stmt(stmt: Option<&Statement<Raw>>) -> bool {
3540    match stmt {
3541        // Add PREPARE to this if we ever support it.
3542        Some(stmt) => matches!(stmt, Statement::Commit(_) | Statement::Rollback(_)),
3543        None => false,
3544    }
3545}
3546
3547#[derive(Debug)]
3548enum FetchResult {
3549    Rows(Option<Box<dyn RowIterator + Send + Sync>>),
3550    Canceled,
3551    Error(ErrorResponse),
3552    Notice(AdapterNotice),
3553}
3554
3555#[derive(Debug)]
3556struct CopyRowScanner {
3557    scan_pos: usize,
3558    last_row_end: Option<usize>,
3559    end_marker_end: Option<usize>,
3560    // Byte offset within `data` at which the in-progress CSV record begins.
3561    // Used to verify the end-of-copy marker against the raw input bytes,
3562    // distinguishing a literal `\.` line from a quoted CSV value `"\."`
3563    // whose decoded form is also `\.`.
3564    record_start: usize,
3565    csv: Option<CsvScanState>,
3566}
3567
3568#[derive(Debug)]
3569struct CsvScanState {
3570    reader: csv_core::Reader,
3571    output: Vec<u8>,
3572    ends: Vec<usize>,
3573    skip_first_record: bool,
3574}
3575
3576impl CopyRowScanner {
3577    fn new(params: &CopyFormatParams<'_>) -> Self {
3578        let csv = match params {
3579            CopyFormatParams::Csv(CopyCsvFormatParams {
3580                delimiter,
3581                quote,
3582                escape,
3583                header,
3584                ..
3585            }) => Some(CsvScanState::new(*delimiter, *quote, *escape, *header)),
3586            _ => None,
3587        };
3588
3589        CopyRowScanner {
3590            scan_pos: 0,
3591            last_row_end: None,
3592            end_marker_end: None,
3593            record_start: 0,
3594            csv,
3595        }
3596    }
3597
3598    fn scan_new_bytes(&mut self, data: &[u8]) {
3599        if self.scan_pos >= data.len() {
3600            return;
3601        }
3602
3603        if let Some(csv) = self.csv.as_mut() {
3604            let mut input = &data[self.scan_pos..];
3605            let mut consumed = 0usize;
3606            while !input.is_empty() {
3607                let (result, n_input, _n_output, _n_ends) =
3608                    csv.reader
3609                        .read_record(input, &mut csv.output, &mut csv.ends);
3610                consumed += n_input;
3611                input = &input[n_input..];
3612
3613                match result {
3614                    ReadRecordResult::InputEmpty => break,
3615                    ReadRecordResult::OutputFull => {
3616                        if n_input == 0 {
3617                            csv.output
3618                                .resize(csv.output.len().saturating_mul(2).max(1), 0);
3619                        }
3620                    }
3621                    ReadRecordResult::OutputEndsFull => {
3622                        if n_input == 0 {
3623                            csv.ends.resize(csv.ends.len().saturating_mul(2).max(1), 0);
3624                        }
3625                    }
3626                    ReadRecordResult::Record | ReadRecordResult::End => {
3627                        let row_end = self.scan_pos + consumed;
3628                        self.last_row_end = Some(row_end);
3629                        if self.end_marker_end.is_none() {
3630                            let is_marker = if csv.skip_first_record {
3631                                csv.skip_first_record = false;
3632                                false
3633                            } else {
3634                                // Detect the marker against the raw input
3635                                // bytes, not the CSV-decoded record. A quoted
3636                                // data row `"\."` decodes to `\.` but must be
3637                                // imported as data; only a bare `\.` line
3638                                // terminates the COPY.
3639                                let raw = &data[self.record_start..row_end];
3640                                // csv-core ends a CRLF record after the `\r`,
3641                                // leaving the trailing `\n` as the leading byte
3642                                // of the next record's span; a CR-only record
3643                                // ends in a lone `\r`. So a `\.` marker record's
3644                                // raw span can be `\.\n` (LF), `\n\.\r` (CRLF)
3645                                // or `\.\r` (CR). Trim CR/LF from both ends
3646                                // before comparing — a trailing-only strip would
3647                                // miss the CRLF/CR forms. Quoted `"\."` data
3648                                // keeps its surrounding quotes after trimming and
3649                                // is therefore correctly rejected.
3650                                let start = raw
3651                                    .iter()
3652                                    .take_while(|&&b| b == b'\r' || b == b'\n')
3653                                    .count();
3654                                let trailing = raw[start..]
3655                                    .iter()
3656                                    .rev()
3657                                    .take_while(|&&b| b == b'\r' || b == b'\n')
3658                                    .count();
3659                                let trimmed = &raw[start..raw.len() - trailing];
3660                                trimmed == b"\\."
3661                            };
3662                            if is_marker {
3663                                self.end_marker_end = Some(row_end);
3664                                self.record_start = row_end;
3665                                break;
3666                            }
3667                        }
3668                        self.record_start = row_end;
3669                    }
3670                }
3671            }
3672        } else {
3673            let mut row_start = self.last_row_end.unwrap_or(0);
3674            for (offset, b) in data[self.scan_pos..].iter().enumerate() {
3675                if *b == b'\n' {
3676                    let row_end = self.scan_pos + offset + 1;
3677                    self.last_row_end = Some(row_end);
3678                    if self.end_marker_end.is_none() {
3679                        let row = &data[row_start..row_end];
3680                        if row.get(0..2) == Some(b"\\.") {
3681                            self.end_marker_end = Some(row_end);
3682                            break;
3683                        }
3684                    }
3685                    row_start = row_end;
3686                }
3687            }
3688        }
3689
3690        self.scan_pos = data.len();
3691    }
3692
3693    fn last_row_end(&self) -> Option<usize> {
3694        self.last_row_end
3695    }
3696
3697    fn end_marker_end(&self) -> Option<usize> {
3698        self.end_marker_end
3699    }
3700
3701    fn current_row_size(&self, data_len: usize) -> usize {
3702        data_len.saturating_sub(self.last_row_end.unwrap_or(0))
3703    }
3704
3705    fn on_split(&mut self, split_pos: usize) {
3706        self.scan_pos = self.scan_pos.saturating_sub(split_pos);
3707        self.last_row_end = None;
3708        self.end_marker_end = self
3709            .end_marker_end
3710            .and_then(|end| end.checked_sub(split_pos));
3711        // `record_start` is only maintained for the CSV path; the text and
3712        // binary paths leave it at 0. For CSV, splits always occur at a
3713        // completed-row boundary, so the in-progress record (if any) starts at
3714        // the new beginning of the buffer. Assert that invariant so the
3715        // `saturating_sub` below doesn't silently paper over a bug that
3716        // bisected an in-progress record — but only when CSV is in use, since
3717        // otherwise `record_start` is meaninglessly 0.
3718        soft_assert_or_log!(
3719            self.csv.is_none() || self.record_start >= split_pos,
3720            "split bisected an in-progress CSV record: record_start={} < split_pos={}",
3721            self.record_start,
3722            split_pos,
3723        );
3724        self.record_start = self.record_start.saturating_sub(split_pos);
3725    }
3726
3727    fn on_truncate(&mut self, new_len: usize) {
3728        self.scan_pos = self.scan_pos.min(new_len);
3729        self.last_row_end = self.last_row_end.filter(|&end| end <= new_len);
3730        self.end_marker_end = self.end_marker_end.filter(|&end| end <= new_len);
3731        self.record_start = self.record_start.min(new_len);
3732    }
3733}
3734
3735impl CsvScanState {
3736    fn new(delimiter: u8, quote: u8, escape: u8, header: bool) -> Self {
3737        let (double_quote, escape) = if quote == escape {
3738            (true, None)
3739        } else {
3740            (false, Some(escape))
3741        };
3742        CsvScanState {
3743            reader: csv_core::ReaderBuilder::new()
3744                .delimiter(delimiter)
3745                .quote(quote)
3746                .double_quote(double_quote)
3747                .escape(escape)
3748                .build(),
3749            output: vec![0; 1],
3750            ends: vec![0; 1],
3751            skip_first_record: header,
3752        }
3753    }
3754}
3755
3756#[cfg(test)]
3757mod test {
3758    use super::*;
3759
3760    #[mz_ore::test]
3761    fn test_copy_row_scanner_end_marker_line_endings() {
3762        // The pgwire COPY row scanner must detect a bare `\.` end-of-copy
3763        // marker for every line ending, and must never mistake a quoted
3764        // `"\."` data row for it. csv-core ends a CRLF record after the `\r`
3765        // (leaving the `\n` as the next record's leading byte), so the raw
3766        // record span of a `\.` marker is `\.\n` (LF), `\n\.\r` (CRLF) or
3767        // `\.\r` (CR); a trailing-only strip would miss the CRLF/CR forms and
3768        // silently import post-marker rows.
3769        let params = CopyFormatParams::Csv(CopyCsvFormatParams::default());
3770
3771        let marker_end = |data: &[u8]| -> Option<usize> {
3772            let mut scanner = CopyRowScanner::new(&params);
3773            scanner.scan_new_bytes(data);
3774            scanner.end_marker_end()
3775        };
3776
3777        for eol in [&b"\n"[..], b"\r\n", b"\r"] {
3778            let join = |lines: &[&str]| -> Vec<u8> {
3779                let mut out = Vec::new();
3780                for line in lines {
3781                    out.extend_from_slice(line.as_bytes());
3782                    out.extend_from_slice(eol);
3783                }
3784                out
3785            };
3786
3787            // Bare `\.` (the marker is the second record, so record_start has
3788            // already advanced past the orphaned terminator of `first`).
3789            // csv-core reports the record after a single terminator byte, so
3790            // the marker boundary sits just past `first<eol>\.` + one byte.
3791            let data = join(&["first", "\\.", "after"]);
3792            let mut prefix = Vec::new();
3793            prefix.extend_from_slice(b"first");
3794            prefix.extend_from_slice(eol);
3795            prefix.extend_from_slice(b"\\.");
3796            assert_eq!(
3797                marker_end(&data),
3798                Some(prefix.len() + 1),
3799                "bare marker, eol={eol:?}"
3800            );
3801
3802            // Quoted "\." is data, not the marker.
3803            let data = join(&["before", "\"\\.\"", "after"]);
3804            assert_eq!(marker_end(&data), None, "quoted marker, eol={eol:?}");
3805        }
3806    }
3807
3808    #[mz_ore::test]
3809    fn test_copy_row_scanner_non_csv_split() {
3810        // Regression: `record_start` is only maintained for the CSV path; the
3811        // text and binary paths leave it at 0. `on_split` must therefore not
3812        // assert `record_start >= split_pos` for those formats — that fires on
3813        // every split of a large text/binary COPY stream (soft-assertions
3814        // panic under test). Mirrors `COPY ... FROM STDIN` (default text
3815        // format) splitting at a row boundary once the buffer fills.
3816        for params in [
3817            CopyFormatParams::Text(CopyTextFormatParams::default()),
3818            CopyFormatParams::Binary,
3819        ] {
3820            let mut scanner = CopyRowScanner::new(&params);
3821            let data = b"1\thello world\t2\tsome text value here\n\
3822                         3\thello world\t6\tsome text value here\n";
3823            scanner.scan_new_bytes(data);
3824            let split_pos = scanner.last_row_end().expect("a complete row");
3825            assert!(split_pos > 0, "params={params:?}");
3826            // Must not panic via the CSV-only `on_split` soft-assert.
3827            scanner.on_split(split_pos);
3828            assert_eq!(scanner.record_start, 0, "params={params:?}");
3829        }
3830    }
3831
3832    #[mz_ore::test]
3833    fn test_parse_options() {
3834        struct TestCase {
3835            input: &'static str,
3836            expect: Result<Vec<(&'static str, &'static str)>, ()>,
3837        }
3838        let tests = vec![
3839            TestCase {
3840                input: "",
3841                expect: Ok(vec![]),
3842            },
3843            TestCase {
3844                input: "--key",
3845                expect: Err(()),
3846            },
3847            TestCase {
3848                input: "--key=val",
3849                expect: Ok(vec![("key", "val")]),
3850            },
3851            TestCase {
3852                input: r#"--key=val -ckey2=val2 -c key3=val3 -c key4=val4 -ckey5=val5"#,
3853                expect: Ok(vec![
3854                    ("key", "val"),
3855                    ("key2", "val2"),
3856                    ("key3", "val3"),
3857                    ("key4", "val4"),
3858                    ("key5", "val5"),
3859                ]),
3860            },
3861            TestCase {
3862                input: r#"-c\ key=val"#,
3863                expect: Ok(vec![(" key", "val")]),
3864            },
3865            TestCase {
3866                input: "--key=val -ckey2 val2",
3867                expect: Err(()),
3868            },
3869            // Unclear what this should do.
3870            TestCase {
3871                input: "--key=",
3872                expect: Ok(vec![("key", "")]),
3873            },
3874        ];
3875        for test in tests {
3876            let got = parse_options(test.input);
3877            let expect = test.expect.map(|r| {
3878                r.into_iter()
3879                    .map(|(k, v)| (k.to_owned(), v.to_owned()))
3880                    .collect()
3881            });
3882            assert_eq!(got, expect, "input: {}", test.input);
3883        }
3884    }
3885
3886    #[mz_ore::test]
3887    fn test_parse_option() {
3888        struct TestCase {
3889            input: &'static str,
3890            expect: Result<(&'static str, &'static str), ()>,
3891        }
3892        let tests = vec![
3893            TestCase {
3894                input: "",
3895                expect: Err(()),
3896            },
3897            TestCase {
3898                input: "--",
3899                expect: Err(()),
3900            },
3901            TestCase {
3902                input: "--c",
3903                expect: Err(()),
3904            },
3905            TestCase {
3906                input: "a=b",
3907                expect: Err(()),
3908            },
3909            TestCase {
3910                input: "--a=b",
3911                expect: Ok(("a", "b")),
3912            },
3913            TestCase {
3914                input: "--ca=b",
3915                expect: Ok(("ca", "b")),
3916            },
3917            TestCase {
3918                input: "-ca=b",
3919                expect: Ok(("a", "b")),
3920            },
3921            // Unclear what this should error, but at least test it.
3922            TestCase {
3923                input: "--=",
3924                expect: Ok(("", "")),
3925            },
3926        ];
3927        for test in tests {
3928            let got = parse_option(test.input);
3929            assert_eq!(got, test.expect, "input: {}", test.input);
3930        }
3931    }
3932
3933    #[mz_ore::test]
3934    fn test_split_options() {
3935        struct TestCase {
3936            input: &'static str,
3937            expect: Vec<&'static str>,
3938        }
3939        let tests = vec![
3940            TestCase {
3941                input: "",
3942                expect: vec![],
3943            },
3944            TestCase {
3945                input: "  ",
3946                expect: vec![],
3947            },
3948            TestCase {
3949                input: " a ",
3950                expect: vec!["a"],
3951            },
3952            TestCase {
3953                input: "  ab     cd   ",
3954                expect: vec!["ab", "cd"],
3955            },
3956            TestCase {
3957                input: r#"  ab\     cd   "#,
3958                expect: vec!["ab ", "cd"],
3959            },
3960            TestCase {
3961                input: r#"  ab\\     cd   "#,
3962                expect: vec![r#"ab\"#, "cd"],
3963            },
3964            TestCase {
3965                input: r#"  ab\\\     cd   "#,
3966                expect: vec![r#"ab\ "#, "cd"],
3967            },
3968            TestCase {
3969                input: r#"  ab\\\ cd   "#,
3970                expect: vec![r#"ab\ cd"#],
3971            },
3972            TestCase {
3973                input: r#"  ab\\\cd   "#,
3974                expect: vec![r#"ab\cd"#],
3975            },
3976            TestCase {
3977                input: r#"a\"#,
3978                expect: vec!["a"],
3979            },
3980            TestCase {
3981                input: r#"a\ "#,
3982                expect: vec!["a "],
3983            },
3984            TestCase {
3985                input: r#"\"#,
3986                expect: vec![],
3987            },
3988            TestCase {
3989                input: r#"\ "#,
3990                expect: vec![r#" "#],
3991            },
3992            TestCase {
3993                input: r#" \ "#,
3994                expect: vec![r#" "#],
3995            },
3996            TestCase {
3997                input: r#"\  "#,
3998                expect: vec![r#" "#],
3999            },
4000        ];
4001        for test in tests {
4002            let got = split_options(test.input);
4003            assert_eq!(got, test.expect, "input: {}", test.input);
4004        }
4005    }
4006
4007    #[mz_ore::test]
4008    fn test_is_jwt() {
4009        // A real JWT header decodes successfully.
4010        assert!(is_jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.signature"));
4011        // Not JWTs: plain strings, wrong segment count, non-JSON headers.
4012        for s in [
4013            "",
4014            "secure_password",
4015            "p4ss.w0rd",
4016            "aaa.bbb.ccc",
4017            "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0",
4018            "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.sig.extra",
4019        ] {
4020            assert!(!is_jwt(s), "is_jwt({s:?})");
4021        }
4022    }
4023}