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