Skip to main content

mz_environmentd/http/
sql.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::net::{IpAddr, SocketAddr};
12use std::pin::pin;
13use std::sync::Arc;
14use std::time::Duration;
15
16use anyhow::anyhow;
17use async_trait::async_trait;
18use axum::extract::connect_info::ConnectInfo;
19use axum::extract::ws::{CloseFrame, Message, Utf8Bytes, WebSocket};
20use axum::extract::{State, WebSocketUpgrade};
21use axum::response::IntoResponse;
22use axum::{Extension, Json};
23use futures::Future;
24use futures::future::BoxFuture;
25
26use http::StatusCode;
27use itertools::Itertools;
28use mz_adapter::client::{RecordFirstRowStream, redact_sql_for_logging};
29use mz_adapter::session::{EndTransactionAction, TransactionStatus};
30use mz_adapter::statement_logging::{StatementEndedExecutionReason, StatementExecutionStrategy};
31use mz_adapter::{
32    AdapterError, AdapterNotice, ExecuteContextGuard, ExecuteResponse, ExecuteResponseKind,
33    PeekResponseUnary, SessionClient, verify_datum_desc,
34};
35use mz_auth::password::Password;
36use mz_catalog::memory::objects::{Cluster, ClusterReplica};
37use mz_interchange::encode::TypedDatum;
38use mz_interchange::json::{JsonNumberPolicy, ToJson};
39use mz_ore::cast::CastFrom;
40use mz_ore::metrics::{MakeCollectorOpts, MetricsRegistry};
41use mz_ore::result::ResultExt;
42use mz_ore::sql::Sql;
43use mz_repr::{Datum, RelationDesc, RowArena, RowIterator};
44use mz_sql::ast::display::AstDisplay;
45use mz_sql::ast::{CopyDirection, CopyStatement, CopyTarget, Raw, Statement, StatementKind};
46use mz_sql::parse::StatementParseResult;
47use mz_sql::plan::Plan;
48use mz_sql::session::metadata::SessionMetadata;
49use prometheus::Opts;
50use prometheus::core::{AtomicF64, GenericGaugeVec};
51use serde::{Deserialize, Serialize};
52use tokio::{select, time};
53use tokio_postgres::error::SqlState;
54use tower_sessions::Session as TowerSession;
55use tracing::{debug, info};
56use tungstenite::protocol::frame::coding::CloseCode;
57
58use crate::http::prometheus::PrometheusSqlQuery;
59use crate::http::{
60    AuthError, AuthedClient, AuthedUser, MAX_REQUEST_SIZE, WsState, ensure_session_unexpired,
61    init_ws, maybe_get_authenticated_session,
62};
63
64#[derive(Debug, thiserror::Error)]
65pub enum Error {
66    #[error(transparent)]
67    Adapter(#[from] AdapterError),
68    #[error(transparent)]
69    Json(#[from] serde_json::Error),
70    #[error(transparent)]
71    Axum(#[from] axum::Error),
72    #[error("SUBSCRIBE only supported over websocket")]
73    SubscribeOnlyOverWs,
74    #[error("current transaction is aborted, commands ignored until end of transaction block")]
75    AbortedTransaction,
76    #[error("unsupported via this API: {0}")]
77    Unsupported(String),
78    #[error("{0}")]
79    Unstructured(anyhow::Error),
80}
81
82impl Error {
83    pub fn detail(&self) -> Option<String> {
84        match self {
85            Error::Adapter(err) => err.detail(),
86            _ => None,
87        }
88    }
89
90    pub fn hint(&self) -> Option<String> {
91        match self {
92            Error::Adapter(err) => err.hint(),
93            _ => None,
94        }
95    }
96
97    pub fn position(&self) -> Option<usize> {
98        match self {
99            Error::Adapter(err) => err.position(),
100            _ => None,
101        }
102    }
103
104    pub fn code(&self) -> SqlState {
105        match self {
106            Error::Adapter(err) => err.code(),
107            Error::AbortedTransaction => SqlState::IN_FAILED_SQL_TRANSACTION,
108            _ => SqlState::INTERNAL_ERROR,
109        }
110    }
111}
112
113static PER_REPLICA_LABELS: &[&str] = &["replica_full_name", "instance_id", "replica_id"];
114
115async fn execute_promsql_query(
116    client: &mut AuthedClient,
117    query: &PrometheusSqlQuery<'_>,
118    metrics_registry: &MetricsRegistry,
119    metrics_by_name: &mut BTreeMap<String, GenericGaugeVec<AtomicF64>>,
120    cluster: Option<(&Cluster, &ClusterReplica)>,
121) {
122    assert_eq!(query.per_replica, cluster.is_some());
123
124    let mut res = SqlResponse {
125        results: Vec::new(),
126    };
127
128    execute_request(client, query.to_sql_request(cluster), &mut res)
129        .await
130        .expect("valid SQL query");
131
132    let result = match res.results.as_slice() {
133        // Each query issued is preceded by several SET commands
134        // to make sure it is routed to the right cluster replica.
135        [
136            SqlResult::Ok { .. },
137            SqlResult::Ok { .. },
138            SqlResult::Ok { .. },
139            result,
140        ] => result,
141        // Transient errors are fine, like if the cluster or replica
142        // was dropped before the promsql query was executed. We
143        // should not see errors in the steady state.
144        _ => {
145            info!(
146                "error executing prometheus query {}: {:?}",
147                query.metric_name, res
148            );
149            return;
150        }
151    };
152
153    let SqlResult::Rows { desc, rows, .. } = result else {
154        info!(
155            "did not receive rows for SQL query for prometheus metric {}: {:?}, {:?}",
156            query.metric_name, result, cluster
157        );
158        return;
159    };
160
161    let gauge_vec = metrics_by_name
162        .entry(query.metric_name.to_string())
163        .or_insert_with(|| {
164            let mut label_names: Vec<String> = desc
165                .columns
166                .iter()
167                .filter(|col| col.name != query.value_column_name)
168                .map(|col| col.name.clone())
169                .collect();
170
171            if query.per_replica {
172                label_names.extend(PER_REPLICA_LABELS.iter().map(|label| label.to_string()));
173            }
174
175            metrics_registry.register::<GenericGaugeVec<AtomicF64>>(MakeCollectorOpts {
176                opts: Opts::new(query.metric_name, query.help).variable_labels(label_names),
177                buckets: None,
178            })
179        });
180
181    for row in rows {
182        // Rows are stored as pre-serialized JSON arrays. Parse each one back to
183        // `Value`s here. Promsql results are tiny, so the parse cost is
184        // negligible.
185        let row: Vec<serde_json::Value> =
186            serde_json::from_str(row.get()).expect("row is a valid JSON array");
187
188        // Non-value columns become Prometheus label values. A SQL `NULL`
189        // arrives as JSON `null` and yields `None` from `as_str()`; fall back
190        // to an empty label rather than panicking. The query author is
191        // responsible for `COALESCE`ing nullable columns to a meaningful
192        // value; this is a defensive backstop.
193        let mut label_values = desc
194            .columns
195            .iter()
196            .zip_eq(&row)
197            .filter(|(col, _)| col.name != query.value_column_name)
198            .map(|(_, val)| val.as_str().unwrap_or(""))
199            .collect::<Vec<_>>();
200
201        let value = desc
202            .columns
203            .iter()
204            .zip_eq(&row)
205            .find(|(col, _)| col.name == query.value_column_name)
206            .map(|(_, val)| val.as_str().unwrap_or("0").parse::<f64>().unwrap_or(0.0))
207            .unwrap_or(0.0);
208
209        match cluster {
210            Some((cluster, replica)) => {
211                let replica_full_name = format!("{}.{}", cluster.name, replica.name);
212                let cluster_id = cluster.id.to_string();
213                let replica_id = replica.replica_id.to_string();
214
215                label_values.push(&replica_full_name);
216                label_values.push(&cluster_id);
217                label_values.push(&replica_id);
218
219                gauge_vec
220                    .get_metric_with_label_values(&label_values)
221                    .expect("valid labels")
222                    .set(value);
223            }
224            None => {
225                gauge_vec
226                    .get_metric_with_label_values(&label_values)
227                    .expect("valid labels")
228                    .set(value);
229            }
230        }
231    }
232}
233
234async fn handle_promsql_query(
235    client: &mut AuthedClient,
236    query: &PrometheusSqlQuery<'_>,
237    metrics_registry: &MetricsRegistry,
238    metrics_by_name: &mut BTreeMap<String, GenericGaugeVec<AtomicF64>>,
239) {
240    if !query.per_replica {
241        execute_promsql_query(client, query, metrics_registry, metrics_by_name, None).await;
242        return;
243    }
244
245    let catalog = client.client.catalog_snapshot("handle_promsql_query").await;
246    let clusters: Vec<&Cluster> = catalog.clusters().collect();
247
248    for cluster in clusters {
249        for replica in cluster.replicas() {
250            execute_promsql_query(
251                client,
252                query,
253                metrics_registry,
254                metrics_by_name,
255                Some((cluster, replica)),
256            )
257            .await;
258        }
259    }
260}
261
262pub async fn handle_promsql(
263    mut client: AuthedClient,
264    queries: &[PrometheusSqlQuery<'_>],
265) -> MetricsRegistry {
266    let metrics_registry = MetricsRegistry::new();
267    let mut metrics_by_name = BTreeMap::new();
268
269    for query in queries {
270        handle_promsql_query(&mut client, query, &metrics_registry, &mut metrics_by_name).await;
271    }
272
273    metrics_registry
274}
275
276pub async fn handle_sql(
277    mut client: AuthedClient,
278    Json(request): Json<SqlRequest>,
279) -> impl IntoResponse {
280    let mut res = SqlResponse {
281        results: Vec::new(),
282    };
283    // Don't need to worry about timeouts or resetting cancel here because there is always exactly 1
284    // request.
285    match execute_request(&mut client, request, &mut res).await {
286        Ok(()) => Ok(Json(res)),
287        Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())),
288    }
289}
290
291#[derive(Debug)]
292pub enum ExistingUser {
293    /// An AuthedUser provided by the
294    /// `x_materialize_user_header_auth` middleware
295    XMaterializeUserHeader(AuthedUser),
296    /// An AuthedUser provided by an authenticated session
297    /// established via [`crate::http::handle_login`].
298    Session(AuthedUser),
299}
300
301pub(crate) async fn handle_sql_ws(
302    State(state): State<WsState>,
303    existing_user: Option<Extension<AuthedUser>>,
304    ws: WebSocketUpgrade,
305    ConnectInfo(addr): ConnectInfo<SocketAddr>,
306    tower_session: Option<Extension<TowerSession>>,
307) -> Result<impl IntoResponse, AuthError> {
308    let session = tower_session.map(|Extension(session)| session);
309    // The `x_materialize_user_header_auth` middleware may have already provided the user for us
310    let user = match existing_user {
311        Some(Extension(user)) => Some(ExistingUser::XMaterializeUserHeader(user)),
312        None => {
313            let session = maybe_get_authenticated_session(session.as_ref()).await;
314            if let Some((session, session_data)) = session {
315                let user = ensure_session_unexpired(session, session_data).await?;
316                Some(ExistingUser::Session(user))
317            } else {
318                None
319            }
320        }
321    };
322
323    let addr = Box::new(addr.ip());
324    Ok(ws
325        .max_message_size(MAX_REQUEST_SIZE)
326        .on_upgrade(|ws| async move { run_ws(state, user, *addr, ws).await }))
327}
328
329#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
330#[serde(untagged)]
331pub enum WebSocketAuth {
332    Basic {
333        user: String,
334        password: Password,
335        #[serde(default)]
336        options: BTreeMap<String, String>,
337    },
338    Bearer {
339        token: String,
340        #[serde(default)]
341        options: BTreeMap<String, String>,
342    },
343    OptionsOnly {
344        #[serde(default)]
345        options: BTreeMap<String, String>,
346    },
347}
348
349async fn run_ws(state: WsState, user: Option<ExistingUser>, peer_addr: IpAddr, mut ws: WebSocket) {
350    let mut client = match init_ws(state, user, peer_addr, &mut ws).await {
351        Ok(client) => client,
352        Err(e) => {
353            // We omit most detail from the error message we send to the client, to
354            // avoid giving attackers unnecessary information during auth. AdapterErrors
355            // are safe to return because they're generated after authentication.
356            debug!("WS request failed init: {}", e);
357            let reason: Utf8Bytes = match e.downcast_ref::<AdapterError>() {
358                Some(error) => error.to_string().into(),
359                None => "unauthorized".to_string().into(),
360            };
361            let _ = ws
362                .send(Message::Close(Some(CloseFrame {
363                    code: CloseCode::Protocol.into(),
364                    reason,
365                })))
366                .await;
367            return;
368        }
369    };
370
371    // Successful auth, send startup messages.
372    let mut msgs = Vec::new();
373    let session = client.client.session();
374    for var in session.vars().notify_set() {
375        msgs.push(WebSocketResponse::ParameterStatus(ParameterStatus {
376            name: var.name().to_string(),
377            value: var.value(),
378        }));
379    }
380    msgs.push(WebSocketResponse::BackendKeyData(BackendKeyData {
381        conn_id: session.conn_id().unhandled(),
382        secret_key: session.secret_key(),
383    }));
384    msgs.push(WebSocketResponse::ReadyForQuery(
385        session.transaction_code().into(),
386    ));
387    for msg in msgs {
388        let _ = ws
389            .send(Message::Text(
390                serde_json::to_string(&msg).expect("must serialize").into(),
391            ))
392            .await;
393    }
394
395    // Send any notices that might have been generated on startup.
396    let notices = session.drain_notices();
397    if let Err(err) = forward_notices(&mut ws, notices).await {
398        debug!("failed to forward notices to WebSocket, {err:?}");
399        return;
400    }
401
402    loop {
403        // Handle timeouts first so we don't execute any statements when there's a pending timeout.
404        let msg = select! {
405            biased;
406
407            // `recv_timeout()` is cancel-safe as per it's docs.
408            Some(timeout) = client.client.recv_timeout() => {
409                client.client.terminate().await;
410                // We must wait for the client to send a request before we can send the error
411                // response. Although this isn't the PG wire protocol, we choose to mirror it by
412                // only sending errors as responses to requests.
413                let _ = ws.recv().await;
414                let err = Error::from(AdapterError::from(timeout));
415                let _ = send_ws_response(&mut ws, WebSocketResponse::Error(err.into())).await;
416                return;
417            },
418            message = ws.recv() => message,
419        };
420
421        client.client.remove_idle_in_transaction_session_timeout();
422
423        let msg = match msg {
424            Some(Ok(msg)) => msg,
425            _ => {
426                // client disconnected
427                return;
428            }
429        };
430
431        let req: Result<SqlRequest, Error> = match msg {
432            Message::Text(data) => serde_json::from_str(&data).err_into(),
433            Message::Binary(data) => serde_json::from_slice(&data).err_into(),
434            // Handled automatically by the server.
435            Message::Ping(_) => {
436                continue;
437            }
438            Message::Pong(_) => {
439                continue;
440            }
441            Message::Close(_) => {
442                return;
443            }
444        };
445
446        // Figure out if we need to send an error, any notices, but always the ready message.
447        let err = match run_ws_request(req, &mut client, &mut ws).await {
448            Ok(()) => None,
449            Err(err) => Some(WebSocketResponse::Error(err.into())),
450        };
451
452        // After running our request, there are several messages we need to send in a
453        // specific order.
454        //
455        // Note: we nest these into a closure so we can centralize our error handling
456        // for when sending over the WebSocket fails. We could also use a try {} block
457        // here, but those aren't stabilized yet.
458        let ws_response = || async {
459            // First respond with any error that might have occurred.
460            if let Some(e_resp) = err {
461                send_ws_response(&mut ws, e_resp).await?;
462            }
463
464            // Then forward along any notices we generated.
465            let notices = client.client.session().drain_notices();
466            forward_notices(&mut ws, notices).await?;
467
468            // Finally, respond that we're ready for the next query.
469            let ready =
470                WebSocketResponse::ReadyForQuery(client.client.session().transaction_code().into());
471            send_ws_response(&mut ws, ready).await?;
472
473            Ok::<_, Error>(())
474        };
475
476        if let Err(err) = ws_response().await {
477            debug!("failed to send response over WebSocket, {err:?}");
478            return;
479        }
480    }
481}
482
483async fn run_ws_request(
484    req: Result<SqlRequest, Error>,
485    client: &mut AuthedClient,
486    ws: &mut WebSocket,
487) -> Result<(), Error> {
488    let req = req?;
489    execute_request(client, req, ws).await
490}
491
492/// Sends a single [`WebSocketResponse`] over the provided [`WebSocket`].
493async fn send_ws_response(ws: &mut WebSocket, resp: WebSocketResponse) -> Result<(), Error> {
494    let msg = serde_json::to_string(&resp).unwrap();
495    let msg = Message::Text(msg.into());
496    ws.send(msg).await?;
497
498    Ok(())
499}
500
501/// Forwards a collection of Notices to the provided [`WebSocket`].
502async fn forward_notices(
503    ws: &mut WebSocket,
504    notices: impl IntoIterator<Item = AdapterNotice>,
505) -> Result<(), Error> {
506    let ws_notices = notices.into_iter().map(|notice| {
507        WebSocketResponse::Notice(Notice {
508            message: notice.to_string(),
509            code: notice.code().code().to_string(),
510            severity: notice.severity().as_str().to_lowercase(),
511            detail: notice.detail(),
512            hint: notice.hint(),
513        })
514    });
515
516    for notice in ws_notices {
517        send_ws_response(ws, notice).await?;
518    }
519
520    Ok(())
521}
522
523/// A request to execute SQL over HTTP or WebSocket. Execution stops at the first statement
524/// that fails.
525#[derive(Serialize, Deserialize, Debug)]
526#[serde(untagged)]
527pub enum SqlRequest {
528    /// One SQL string without parameters, as in PostgreSQL's simple query protocol. Its
529    /// statements, separated by semicolons, share one implicit transaction, unless `BEGIN` and
530    /// `COMMIT` among them group statements into an explicit transaction.
531    Simple { query: Sql },
532    /// A list of statements, each with its own parameters. Each statement runs in its own
533    /// implicit transaction, as if it were followed by a `Sync` in PostgreSQL's extended query
534    /// protocol, unless `BEGIN` and `COMMIT` entries group statements into an explicit transaction.
535    Extended { queries: Vec<ExtendedRequest> },
536}
537
538/// One statement of an [`SqlRequest::Extended`].
539#[derive(Serialize, Deserialize, Debug)]
540pub struct ExtendedRequest {
541    /// A SQL string containing exactly one statement.
542    query: String,
543    /// The values of the statement's parameters, as text. `None` is SQL `NULL`.
544    #[serde(default)]
545    params: Vec<Option<String>>,
546}
547
548/// The response to a `SqlRequest`.
549#[derive(Debug, Serialize, Deserialize)]
550pub struct SqlResponse {
551    /// The results for each query in the request.
552    pub(in crate::http) results: Vec<SqlResult>,
553}
554
555impl SqlResponse {
556    /// Creates a new empty SqlResponse for collecting results.
557    pub(in crate::http) fn new() -> Self {
558        Self {
559            results: Vec::new(),
560        }
561    }
562}
563
564pub(in crate::http) enum StatementResult {
565    SqlResult(SqlResult),
566    /// A peek (`SELECT`) result whose rows are streamed out of the peek response
567    /// stash. The stream is consumed lazily by the sender so a large result is
568    /// not buffered whole. Contrast `SqlResult::Rows`, which holds
569    /// already-collected rows and exists only for the buffered JSON transport.
570    Rows {
571        desc: RelationDesc,
572        rows_stream: RecordFirstRowStream,
573        max_result_size: usize,
574    },
575    Subscribe {
576        desc: RelationDesc,
577        tag: String,
578        rx: RecordFirstRowStream,
579        ctx_extra: ExecuteContextGuard,
580    },
581}
582
583impl From<SqlResult> for StatementResult {
584    fn from(inner: SqlResult) -> Self {
585        Self::SqlResult(inner)
586    }
587}
588
589/// The result of a single query in a [`SqlResponse`].
590#[derive(Debug, Serialize, Deserialize)]
591#[serde(untagged)]
592pub enum SqlResult {
593    /// The query returned rows.
594    Rows {
595        /// The command complete tag.
596        tag: String,
597        /// The result rows, each already serialized to a compact JSON array.
598        ///
599        /// We accumulate pre-serialized rows rather than a `serde_json::Value`
600        /// tree so the buffered footprint stays close to the wire size. A
601        /// `Value` tree is ~10-15x larger (each cell a heap `Value`, in a
602        /// per-row `Vec`, in the outer `Vec`), which is what let a large
603        /// `SELECT` over the JSON endpoint OOM the process. `RawValue`
604        /// re-serializes verbatim, so the wire format is unchanged.
605        rows: Vec<Box<serde_json::value::RawValue>>,
606        /// Information about each column.
607        desc: Description,
608        // Any notices generated during execution of the query.
609        notices: Vec<Notice>,
610    },
611    /// The query executed successfully but did not return rows.
612    Ok {
613        /// The command complete tag.
614        ok: String,
615        /// Any notices generated during execution of the query.
616        notices: Vec<Notice>,
617        /// Any parameters that may have changed.
618        ///
619        /// Note: skip serializing this field in a response if the list of parameters is empty.
620        #[serde(skip_serializing_if = "Vec::is_empty")]
621        parameters: Vec<ParameterStatus>,
622    },
623    /// The query returned an error.
624    Err {
625        error: SqlError,
626        // Any notices generated during execution of the query.
627        notices: Vec<Notice>,
628    },
629}
630
631impl SqlResult {
632    /// Convert adapter Row results into the buffered web row result format. Error
633    /// if the row format does not match the expected descriptor, or if the
634    /// result exceeds `max_query_result_size`.
635    ///
636    /// This buffers the whole result, so it is used only by the JSON transport,
637    /// whose response is a single document. The WebSocket transport streams rows
638    /// through `StatementResult::Rows` and never calls this. The size guard
639    /// counts `Row::byte_len`, matching the WebSocket transport and pgwire, and
640    /// rows are stored as pre-serialized compact `RawValue`s to avoid the
641    /// amplified `Value`-tree buffering that could OOM.
642    async fn rows<S>(
643        sender: &mut S,
644        client: &mut SessionClient,
645        mut rows_stream: RecordFirstRowStream,
646        max_query_result_size: usize,
647        desc: &RelationDesc,
648    ) -> Result<SqlResult, Error>
649    where
650        S: ResultSender,
651    {
652        let mut rows: Vec<Box<serde_json::value::RawValue>> = vec![];
653        let mut datum_vec = mz_repr::DatumVec::new();
654        let types = &desc.typ().column_types;
655
656        let mut query_result_size: usize = 0;
657
658        loop {
659            let peek_response = tokio::select! {
660                notice = client.session().recv_notice(), if S::SUPPORTS_STREAMING_NOTICES => {
661                    sender.emit_streaming_notices(vec![notice]).await?;
662                    continue;
663                }
664                e = sender.connection_error() => return Err(e),
665                r = rows_stream.recv() => {
666                    match r {
667                        Some(r) => r,
668                        None => break,
669                    }
670                },
671            };
672
673            let mut sql_rows = match peek_response {
674                PeekResponseUnary::Rows(rows) => rows,
675                PeekResponseUnary::Error(e) => {
676                    return Ok(SqlResult::err(client, e));
677                }
678                PeekResponseUnary::DependencyDropped(dep) => {
679                    return Ok(SqlResult::err(client, dep.to_concurrent_dependency_drop()));
680                }
681                PeekResponseUnary::Canceled => {
682                    return Ok(SqlResult::err(client, AdapterError::Canceled));
683                }
684            };
685
686            if let Err(err) = verify_datum_desc(desc, &mut sql_rows) {
687                return Ok(SqlResult::Err {
688                    error: err.into(),
689                    notices: make_notices(client),
690                });
691            }
692
693            while let Some(row) = sql_rows.next() {
694                // Enforce `max_result_size` on `Row::byte_len`, the same quantity
695                // pgwire and the WebSocket transport use, so the cap means one
696                // thing across every transport.
697                query_result_size = query_result_size.saturating_add(row.byte_len());
698                if query_result_size > max_query_result_size {
699                    use bytesize::ByteSize;
700                    return Ok(SqlResult::err(
701                        client,
702                        AdapterError::ResultSize(format!(
703                            "result exceeds max size of {}",
704                            ByteSize::b(u64::cast_from(max_query_result_size))
705                        )),
706                    ));
707                }
708                let datums = datum_vec.borrow_with(row);
709                let json_row: Vec<serde_json::Value> = datums
710                    .iter()
711                    .enumerate()
712                    .map(|(i, d)| {
713                        TypedDatum::new(*d, &types[i])
714                            .json(&JsonNumberPolicy::ConvertNumberToString)
715                    })
716                    .collect();
717                // Keep only the compact serialized JSON text. The transient
718                // `Value` tree above is dropped per row, so at most one row's tree
719                // is resident, avoiding the amplified buffering that could OOM.
720                let raw = serde_json::value::to_raw_value(&json_row)
721                    .expect("row of JSON values always serializes");
722                rows.push(raw);
723            }
724        }
725
726        let tag = format!("SELECT {}", rows.len());
727        Ok(SqlResult::Rows {
728            tag,
729            rows,
730            desc: Description::from(desc),
731            notices: make_notices(client),
732        })
733    }
734
735    fn err(client: &mut SessionClient, error: impl Into<SqlError>) -> SqlResult {
736        SqlResult::Err {
737            error: error.into(),
738            notices: make_notices(client),
739        }
740    }
741
742    fn ok(client: &mut SessionClient, tag: String, params: Vec<ParameterStatus>) -> SqlResult {
743        SqlResult::Ok {
744            ok: tag,
745            parameters: params,
746            notices: make_notices(client),
747        }
748    }
749}
750
751#[derive(Debug, Deserialize, Serialize)]
752pub struct SqlError {
753    pub message: String,
754    pub code: String,
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub detail: Option<String>,
757    #[serde(skip_serializing_if = "Option::is_none")]
758    pub hint: Option<String>,
759    #[serde(skip_serializing_if = "Option::is_none")]
760    pub position: Option<usize>,
761}
762
763impl From<Error> for SqlError {
764    fn from(err: Error) -> Self {
765        SqlError {
766            message: err.to_string(),
767            code: err.code().code().to_string(),
768            detail: err.detail(),
769            hint: err.hint(),
770            position: err.position(),
771        }
772    }
773}
774
775impl From<AdapterError> for SqlError {
776    fn from(value: AdapterError) -> Self {
777        Error::from(value).into()
778    }
779}
780
781#[derive(Debug, Deserialize, Serialize)]
782#[serde(tag = "type", content = "payload")]
783pub enum WebSocketResponse {
784    ReadyForQuery(String),
785    Notice(Notice),
786    Rows(Description),
787    Row(Vec<serde_json::Value>),
788    CommandStarting(CommandStarting),
789    CommandComplete(String),
790    Error(SqlError),
791    ParameterStatus(ParameterStatus),
792    BackendKeyData(BackendKeyData),
793}
794
795#[derive(Debug, Serialize, Deserialize)]
796pub struct Notice {
797    message: String,
798    code: String,
799    severity: String,
800    #[serde(skip_serializing_if = "Option::is_none")]
801    pub detail: Option<String>,
802    #[serde(skip_serializing_if = "Option::is_none")]
803    pub hint: Option<String>,
804}
805
806impl Notice {
807    pub fn message(&self) -> &str {
808        &self.message
809    }
810}
811
812#[derive(Debug, Serialize, Deserialize)]
813pub struct Description {
814    pub columns: Vec<Column>,
815}
816
817impl From<&RelationDesc> for Description {
818    fn from(desc: &RelationDesc) -> Self {
819        let columns = desc
820            .iter()
821            .map(|(name, typ)| {
822                let pg_type = mz_pgrepr::Type::from(&typ.scalar_type);
823                Column {
824                    name: name.to_string(),
825                    type_oid: pg_type.oid(),
826                    type_len: pg_type.typlen(),
827                    type_mod: pg_type.typmod(),
828                }
829            })
830            .collect();
831        Description { columns }
832    }
833}
834
835#[derive(Debug, Serialize, Deserialize)]
836pub struct Column {
837    pub name: String,
838    pub type_oid: u32,
839    pub type_len: i16,
840    pub type_mod: i32,
841}
842
843#[derive(Debug, Serialize, Deserialize)]
844pub struct ParameterStatus {
845    name: String,
846    value: String,
847}
848
849#[derive(Debug, Serialize, Deserialize)]
850pub struct BackendKeyData {
851    conn_id: u32,
852    secret_key: u32,
853}
854
855#[derive(Debug, Serialize, Deserialize)]
856pub struct CommandStarting {
857    has_rows: bool,
858    is_streaming: bool,
859}
860
861/// Trait describing how to transmit a response to a client. HTTP clients
862/// accumulate into a Vec and send all at once. WebSocket clients send each
863/// message as they occur.
864#[async_trait]
865pub(in crate::http) trait ResultSender: Send {
866    const SUPPORTS_STREAMING_NOTICES: bool = false;
867
868    /// Adds a result to the client. The first component of the return value is
869    /// Err if sending to the client
870    /// produced an error and the server should disconnect. It is Ok(Err) if the statement
871    /// produced an error and should error the transaction, but remain connected. It is Ok(Ok(()))
872    /// if the statement succeeded.
873    /// The second component of the return value is `Some` if execution still
874    /// needs to be retired for statement logging purposes.
875    async fn add_result(
876        &mut self,
877        client: &mut SessionClient,
878        res: StatementResult,
879    ) -> (
880        Result<Result<(), ()>, Error>,
881        Option<(StatementEndedExecutionReason, ExecuteContextGuard)>,
882    );
883
884    /// Returns a future that resolves only when the client connection has gone away.
885    fn connection_error(&mut self) -> BoxFuture<'_, Error>;
886    /// Reports whether the client supports streaming SUBSCRIBE results.
887    fn allow_subscribe(&self) -> bool;
888
889    /// Emits a streaming notice if the sender supports it.
890    ///
891    /// Does nothing if `SUPPORTS_STREAMING_NOTICES` is false.
892    async fn emit_streaming_notices(&mut self, _: Vec<AdapterNotice>) -> Result<(), Error> {
893        unreachable!("streaming notices marked as unsupported")
894    }
895}
896
897#[async_trait]
898impl ResultSender for SqlResponse {
899    // The first component of the return value is
900    // Err if sending to the client
901    // produced an error and the server should disconnect. It is Ok(Err) if the statement
902    // produced an error and should error the transaction, but remain connected. It is Ok(Ok(()))
903    // if the statement succeeded.
904    // The second component of the return value is `Some` if execution still
905    // needs to be retired for statement logging purposes.
906    async fn add_result(
907        &mut self,
908        client: &mut SessionClient,
909        res: StatementResult,
910    ) -> (
911        Result<Result<(), ()>, Error>,
912        Option<(StatementEndedExecutionReason, ExecuteContextGuard)>,
913    ) {
914        let (res, stmt_logging) = match res {
915            StatementResult::SqlResult(res) => {
916                let is_err = matches!(res, SqlResult::Err { .. });
917                self.results.push(res);
918                let res = if is_err { Err(()) } else { Ok(()) };
919                (res, None)
920            }
921            StatementResult::Rows {
922                desc,
923                rows_stream,
924                max_result_size,
925            } => {
926                // The JSON transport is a single buffered document, so the rows
927                // must be collected before the response is serialized.
928                // `SqlResult::rows` bounds that buffer against `max_result_size`.
929                let res = match SqlResult::rows(self, client, rows_stream, max_result_size, &desc)
930                    .await
931                {
932                    Ok(res) => res,
933                    Err(e) => return (Err(e), None),
934                };
935                let is_err = matches!(res, SqlResult::Err { .. });
936                self.results.push(res);
937                let res = if is_err { Err(()) } else { Ok(()) };
938                (res, None)
939            }
940            StatementResult::Subscribe { ctx_extra, .. } => {
941                let message = "SUBSCRIBE only supported over websocket";
942                self.results.push(SqlResult::Err {
943                    error: Error::SubscribeOnlyOverWs.into(),
944                    notices: Vec::new(),
945                });
946                (
947                    Err(()),
948                    Some((
949                        StatementEndedExecutionReason::Errored {
950                            error: message.into(),
951                        },
952                        ctx_extra,
953                    )),
954                )
955            }
956        };
957        (Ok(res), stmt_logging)
958    }
959
960    fn connection_error(&mut self) -> BoxFuture<'_, Error> {
961        Box::pin(futures::future::pending())
962    }
963
964    fn allow_subscribe(&self) -> bool {
965        false
966    }
967}
968
969#[async_trait]
970impl ResultSender for WebSocket {
971    const SUPPORTS_STREAMING_NOTICES: bool = true;
972
973    // The first component of the return value is Err if sending to the client produced an error and
974    // the server should disconnect. It is Ok(Err) if the statement produced an error and should
975    // error the transaction, but remain connected. It is Ok(Ok(())) if the statement succeeded. The
976    // second component of the return value is `Some` if execution still needs to be retired for
977    // statement logging purposes.
978    async fn add_result(
979        &mut self,
980        client: &mut SessionClient,
981        res: StatementResult,
982    ) -> (
983        Result<Result<(), ()>, Error>,
984        Option<(StatementEndedExecutionReason, ExecuteContextGuard)>,
985    ) {
986        let (has_rows, is_streaming) = match res {
987            StatementResult::SqlResult(SqlResult::Err { .. }) => (false, false),
988            StatementResult::SqlResult(SqlResult::Ok { .. }) => (false, false),
989            StatementResult::SqlResult(SqlResult::Rows { .. }) => (true, false),
990            StatementResult::Rows { .. } => (true, false),
991            StatementResult::Subscribe { .. } => (true, true),
992        };
993        if let Err(e) = send_ws_response(
994            self,
995            WebSocketResponse::CommandStarting(CommandStarting {
996                has_rows,
997                is_streaming,
998            }),
999        )
1000        .await
1001        {
1002            return (Err(e), None);
1003        }
1004
1005        let (is_err, msgs, stmt_logging) = match res {
1006            StatementResult::SqlResult(SqlResult::Rows { .. }) => {
1007                // `SqlResult::Rows` holds already-collected rows for the buffered
1008                // JSON transport only. The WebSocket transport streams peek
1009                // results through `StatementResult::Rows` and never materializes
1010                // a `SqlResult::Rows`.
1011                unreachable!("WebSocket streams peek rows via StatementResult::Rows")
1012            }
1013            StatementResult::Rows {
1014                ref desc,
1015                mut rows_stream,
1016                max_result_size,
1017            } => match stream_ws_peek_rows(self, client, desc, &mut rows_stream, max_result_size)
1018                .await
1019            {
1020                Ok(result) => result,
1021                // A write failure means the remote broke the connection, which we
1022                // treat as a cancellation to match pgwire.
1023                Err(e) => return (Err(e), None),
1024            },
1025            StatementResult::SqlResult(SqlResult::Ok {
1026                ok,
1027                parameters,
1028                notices,
1029            }) => {
1030                let mut msgs = vec![WebSocketResponse::CommandComplete(ok)];
1031                msgs.extend(notices.into_iter().map(WebSocketResponse::Notice));
1032                msgs.extend(
1033                    parameters
1034                        .into_iter()
1035                        .map(WebSocketResponse::ParameterStatus),
1036                );
1037                (false, msgs, None)
1038            }
1039            StatementResult::SqlResult(SqlResult::Err { error, notices }) => {
1040                let mut msgs = vec![WebSocketResponse::Error(error)];
1041                msgs.extend(notices.into_iter().map(WebSocketResponse::Notice));
1042                (true, msgs, None)
1043            }
1044            StatementResult::Subscribe {
1045                ref desc,
1046                tag,
1047                mut rx,
1048                ctx_extra,
1049            } => {
1050                if let Err(e) = send_ws_response(self, WebSocketResponse::Rows(desc.into())).await {
1051                    // We consider the remote breaking the connection to be a cancellation,
1052                    // matching the behavior for pgwire
1053                    return (
1054                        Err(e),
1055                        Some((StatementEndedExecutionReason::Canceled, ctx_extra)),
1056                    );
1057                }
1058
1059                let mut datum_vec = mz_repr::DatumVec::new();
1060                let mut result_size: usize = 0;
1061                let mut rows_returned = 0;
1062                loop {
1063                    let res = match await_rows(self, client, rx.recv()).await {
1064                        Ok(res) => res,
1065                        Err(e) => {
1066                            // We consider the remote breaking the connection to be a cancellation,
1067                            // matching the behavior for pgwire
1068                            return (
1069                                Err(e),
1070                                Some((StatementEndedExecutionReason::Canceled, ctx_extra)),
1071                            );
1072                        }
1073                    };
1074                    match res {
1075                        Some(PeekResponseUnary::Rows(mut rows)) => {
1076                            if let Err(err) = verify_datum_desc(desc, &mut rows) {
1077                                let error = err.to_string();
1078                                break (
1079                                    true,
1080                                    vec![WebSocketResponse::Error(err.into())],
1081                                    Some((
1082                                        StatementEndedExecutionReason::Errored { error },
1083                                        ctx_extra,
1084                                    )),
1085                                );
1086                            }
1087
1088                            rows_returned += rows.count();
1089                            while let Some(row) = rows.next() {
1090                                result_size = result_size.saturating_add(row.byte_len());
1091                                let datums = datum_vec.borrow_with(row);
1092                                let types = &desc.typ().column_types;
1093                                if let Err(e) = send_ws_response(
1094                                    self,
1095                                    WebSocketResponse::Row(
1096                                        datums
1097                                            .iter()
1098                                            .enumerate()
1099                                            .map(|(i, d)| {
1100                                                TypedDatum::new(*d, &types[i])
1101                                                    .json(&JsonNumberPolicy::ConvertNumberToString)
1102                                            })
1103                                            .collect(),
1104                                    ),
1105                                )
1106                                .await
1107                                {
1108                                    // We consider the remote breaking the connection to be a cancellation,
1109                                    // matching the behavior for pgwire
1110                                    return (
1111                                        Err(e),
1112                                        Some((StatementEndedExecutionReason::Canceled, ctx_extra)),
1113                                    );
1114                                }
1115                            }
1116                        }
1117                        Some(PeekResponseUnary::Error(err)) => {
1118                            let error = err.to_string();
1119                            break (
1120                                true,
1121                                vec![WebSocketResponse::Error(err.into())],
1122                                Some((StatementEndedExecutionReason::Errored { error }, ctx_extra)),
1123                            );
1124                        }
1125                        Some(PeekResponseUnary::DependencyDropped(dep)) => {
1126                            let err = dep.to_concurrent_dependency_drop();
1127                            let error = err.to_string();
1128                            break (
1129                                true,
1130                                vec![WebSocketResponse::Error(err.into())],
1131                                Some((StatementEndedExecutionReason::Errored { error }, ctx_extra)),
1132                            );
1133                        }
1134                        Some(PeekResponseUnary::Canceled) => {
1135                            break (
1136                                true,
1137                                vec![WebSocketResponse::Error(AdapterError::Canceled.into())],
1138                                Some((StatementEndedExecutionReason::Canceled, ctx_extra)),
1139                            );
1140                        }
1141                        None => {
1142                            break (
1143                                false,
1144                                vec![WebSocketResponse::CommandComplete(tag)],
1145                                Some((
1146                                    StatementEndedExecutionReason::Success {
1147                                        result_size: Some(u64::cast_from(result_size)),
1148                                        rows_returned: Some(u64::cast_from(rows_returned)),
1149                                        execution_strategy: Some(
1150                                            StatementExecutionStrategy::Standard,
1151                                        ),
1152                                    },
1153                                    ctx_extra,
1154                                )),
1155                            );
1156                        }
1157                    }
1158                }
1159            }
1160        };
1161        for msg in msgs {
1162            if let Err(e) = send_ws_response(self, msg).await {
1163                return (
1164                    Err(e),
1165                    stmt_logging.map(|(_old_reason, ctx_extra)| {
1166                        (StatementEndedExecutionReason::Canceled, ctx_extra)
1167                    }),
1168                );
1169            }
1170        }
1171        (Ok(if is_err { Err(()) } else { Ok(()) }), stmt_logging)
1172    }
1173
1174    // Send a websocket Ping every second to verify the client is still
1175    // connected.
1176    fn connection_error(&mut self) -> BoxFuture<'_, Error> {
1177        Box::pin(async {
1178            let mut tick = time::interval(Duration::from_secs(1));
1179            tick.tick().await;
1180            loop {
1181                tick.tick().await;
1182                if let Err(err) = self.send(Message::Ping(Vec::new().into())).await {
1183                    return err.into();
1184                }
1185            }
1186        })
1187    }
1188
1189    fn allow_subscribe(&self) -> bool {
1190        true
1191    }
1192
1193    async fn emit_streaming_notices(&mut self, notices: Vec<AdapterNotice>) -> Result<(), Error> {
1194        forward_notices(self, notices).await
1195    }
1196}
1197
1198async fn await_rows<S, F, R>(sender: &mut S, client: &mut SessionClient, f: F) -> Result<R, Error>
1199where
1200    S: ResultSender,
1201    F: Future<Output = R> + Send,
1202{
1203    let mut f = pin!(f);
1204    loop {
1205        tokio::select! {
1206            notice = client.session().recv_notice(), if S::SUPPORTS_STREAMING_NOTICES => {
1207                sender.emit_streaming_notices(vec![notice]).await?;
1208            }
1209            e = sender.connection_error() => return Err(e),
1210            r = &mut f => return Ok(r),
1211        }
1212    }
1213}
1214
1215/// Streams a peek (`SELECT`) result to a WebSocket client one stash batch at a
1216/// time, flushing between batches so a slow client applies real backpressure
1217/// and only one batch is resident. This gives a large `SELECT` over WebSocket
1218/// the same bounded memory profile as pgwire `SELECT`, and mirrors the
1219/// `Subscribe` arm of `WebSocket::add_result`.
1220///
1221/// On success returns the `(is_err, msgs, stmt_logging)` triple that
1222/// `add_result` folds into its response. An `Err` means a write to the socket
1223/// failed, so the server should disconnect. `add_result` turns that into a
1224/// cancellation to match pgwire.
1225///
1226/// The `Rows` descriptor is sent lazily, right before the first batch of rows
1227/// or an empty successful result, but never before an error. So a query that
1228/// fails before producing any rows emits only an `Error`.
1229async fn stream_ws_peek_rows(
1230    ws: &mut WebSocket,
1231    client: &mut SessionClient,
1232    desc: &RelationDesc,
1233    rows_stream: &mut RecordFirstRowStream,
1234    max_result_size: usize,
1235) -> Result<
1236    (
1237        bool,
1238        Vec<WebSocketResponse>,
1239        Option<(StatementEndedExecutionReason, ExecuteContextGuard)>,
1240    ),
1241    Error,
1242> {
1243    let mut datum_vec = mz_repr::DatumVec::new();
1244    let mut result_size: usize = 0;
1245    let mut rows_returned: usize = 0;
1246    let mut sent_rows_desc = false;
1247    loop {
1248        // Bind before matching so the `Option<PeekResponseUnary>` (which has a
1249        // significant `Drop`) is not a temporary living for the whole match.
1250        let res = await_rows(ws, client, rows_stream.recv()).await?;
1251        match res {
1252            Some(PeekResponseUnary::Rows(mut rows)) => {
1253                if let Err(err) = verify_datum_desc(desc, &mut rows) {
1254                    return Ok(ws_peek_result(
1255                        client,
1256                        true,
1257                        vec![WebSocketResponse::Error(err.into())],
1258                    ));
1259                }
1260                // The header waits until a batch has passed `verify_datum_desc`,
1261                // so a query that fails before producing any rows emits only an
1262                // `Error`. Sending it before the loop, as the `Subscribe` arm
1263                // does, would put a `Rows` header in front of that `Error`.
1264                if !sent_rows_desc {
1265                    send_ws_response(ws, WebSocketResponse::Rows(desc.into())).await?;
1266                    sent_rows_desc = true;
1267                }
1268                let types = &desc.typ().column_types;
1269                while let Some(row) = rows.next() {
1270                    result_size = result_size.saturating_add(row.byte_len());
1271                    if result_size > max_result_size {
1272                        use bytesize::ByteSize;
1273                        return Ok(ws_peek_result(
1274                            client,
1275                            true,
1276                            vec![WebSocketResponse::Error(
1277                                AdapterError::ResultSize(format!(
1278                                    "result exceeds max size of {}",
1279                                    ByteSize::b(u64::cast_from(max_result_size))
1280                                ))
1281                                .into(),
1282                            )],
1283                        ));
1284                    }
1285                    let datums = datum_vec.borrow_with(row);
1286                    send_ws_response(
1287                        ws,
1288                        WebSocketResponse::Row(
1289                            datums
1290                                .iter()
1291                                .enumerate()
1292                                .map(|(i, d)| {
1293                                    TypedDatum::new(*d, &types[i])
1294                                        .json(&JsonNumberPolicy::ConvertNumberToString)
1295                                })
1296                                .collect(),
1297                        ),
1298                    )
1299                    .await?;
1300                    rows_returned += 1;
1301                }
1302            }
1303            Some(PeekResponseUnary::Error(error)) => {
1304                return Ok(ws_peek_result(
1305                    client,
1306                    true,
1307                    vec![WebSocketResponse::Error(error.into())],
1308                ));
1309            }
1310            Some(PeekResponseUnary::DependencyDropped(dep)) => {
1311                return Ok(ws_peek_result(
1312                    client,
1313                    true,
1314                    vec![WebSocketResponse::Error(
1315                        dep.to_concurrent_dependency_drop().into(),
1316                    )],
1317                ));
1318            }
1319            Some(PeekResponseUnary::Canceled) => {
1320                return Ok(ws_peek_result(
1321                    client,
1322                    true,
1323                    vec![WebSocketResponse::Error(AdapterError::Canceled.into())],
1324                ));
1325            }
1326            None => {
1327                // An empty successful result still owes the client a `Rows`
1328                // descriptor before `CommandComplete`.
1329                if !sent_rows_desc {
1330                    send_ws_response(ws, WebSocketResponse::Rows(desc.into())).await?;
1331                }
1332                return Ok(ws_peek_result(
1333                    client,
1334                    false,
1335                    vec![WebSocketResponse::CommandComplete(format!(
1336                        "SELECT {rows_returned}"
1337                    ))],
1338                ));
1339            }
1340        }
1341    }
1342}
1343
1344/// Packages a terminal result of `stream_ws_peek_rows`, appending any notices
1345/// still buffered on the session after `msgs`.
1346///
1347/// The streaming loop forwards notices live through `await_rows`' `recv_notice`
1348/// select, but the terminal `recv() -> None` can race a still-buffered notice
1349/// and end the loop first. Draining here restores the buffered path's guarantee
1350/// that all notices reach the client, after `CommandComplete` or `Error`.
1351fn ws_peek_result(
1352    client: &mut SessionClient,
1353    is_err: bool,
1354    mut msgs: Vec<WebSocketResponse>,
1355) -> (
1356    bool,
1357    Vec<WebSocketResponse>,
1358    Option<(StatementEndedExecutionReason, ExecuteContextGuard)>,
1359) {
1360    msgs.extend(
1361        make_notices(client)
1362            .into_iter()
1363            .map(WebSocketResponse::Notice),
1364    );
1365    (is_err, msgs, None)
1366}
1367
1368async fn send_and_retire<S: ResultSender>(
1369    res: StatementResult,
1370    client: &mut SessionClient,
1371    sender: &mut S,
1372) -> Result<Result<(), ()>, Error> {
1373    let (res, stmt_logging) = sender.add_result(client, res).await;
1374    if let Some((reason, ctx_extra)) = stmt_logging {
1375        client.retire_execute(ctx_extra, reason);
1376    }
1377    res
1378}
1379
1380/// Returns Ok(Err) if any statement error'd during execution.
1381async fn execute_stmt_group<S: ResultSender>(
1382    client: &mut SessionClient,
1383    sender: &mut S,
1384    stmt_group: Vec<(Statement<Raw>, String, Vec<Option<String>>)>,
1385) -> Result<Result<(), ()>, Error> {
1386    let num_stmts = stmt_group.len();
1387    for (stmt, sql, params) in stmt_group {
1388        assert!(
1389            num_stmts <= 1 || params.is_empty(),
1390            "statement groups contain more than 1 statement iff Simple request, which does not support parameters"
1391        );
1392
1393        let is_aborted_txn = matches!(client.session().transaction(), TransactionStatus::Failed(_));
1394        if is_aborted_txn && !is_txn_exit_stmt(&stmt) {
1395            let err = SqlResult::err(client, Error::AbortedTransaction);
1396            let _ = send_and_retire(err.into(), client, sender).await?;
1397            return Ok(Err(()));
1398        }
1399
1400        // Mirror the behavior of the PostgreSQL simple query protocol.
1401        // See the pgwire::protocol::StateMachine::query method for details.
1402        if let Err(e) = client.start_transaction(Some(num_stmts)) {
1403            let err = SqlResult::err(client, e);
1404            let _ = send_and_retire(err.into(), client, sender).await?;
1405            return Ok(Err(()));
1406        }
1407        let res = execute_stmt(client, sender, stmt, sql, params).await?;
1408        let is_err = send_and_retire(res, client, sender).await?;
1409
1410        if is_err.is_err() {
1411            // Mirror StateMachine::error, which sometimes will clean up the
1412            // transaction state instead of always leaving it in Failed.
1413            let txn = client.session().transaction();
1414            match txn {
1415                // Error can be called from describe and parse and so might not be in an active
1416                // transaction.
1417                TransactionStatus::Default | TransactionStatus::Failed(_) => {}
1418                // In Started (i.e., a single statement) and implicit transactions cleanup themselves.
1419                TransactionStatus::Started(_) | TransactionStatus::InTransactionImplicit(_) => {
1420                    if let Err(err) = client.end_transaction(EndTransactionAction::Rollback).await {
1421                        let err = SqlResult::err(client, err);
1422                        let _ = send_and_retire(err.into(), client, sender).await?;
1423                    }
1424                }
1425                // Explicit transactions move to failed.
1426                TransactionStatus::InTransaction(_) => {
1427                    client.fail_transaction();
1428                }
1429            }
1430            return Ok(Err(()));
1431        }
1432    }
1433    Ok(Ok(()))
1434}
1435
1436/// Executes an entire [`SqlRequest`].
1437///
1438/// See the user-facing documentation about the HTTP API for a description of
1439/// the semantics of this function.
1440/// Executes a SQL request and sends results to the provided sender.
1441///
1442/// Made visible to http submodules (like mcp) via `pub(in crate::http)` to allow
1443/// reuse of SQL execution logic.
1444pub(in crate::http) async fn execute_request<S: ResultSender>(
1445    client: &mut AuthedClient,
1446    request: SqlRequest,
1447    sender: &mut S,
1448) -> Result<(), Error> {
1449    let client = &mut client.client;
1450
1451    if client.statement_arrival_logging_enabled().await {
1452        let session = client.session();
1453        let conn_id = session.conn_id();
1454        let session_uuid = session.uuid();
1455        match &request {
1456            SqlRequest::Simple { query } => {
1457                info!(
1458                    %conn_id, %session_uuid, kind = "http_simple",
1459                    sql = %redact_sql_for_logging(query.as_str()),
1460                    "statement arrival"
1461                );
1462            }
1463            SqlRequest::Extended { queries } => {
1464                for ExtendedRequest { query, params } in queries {
1465                    // Parameter values are data that redaction cannot reach,
1466                    // so only their count is logged.
1467                    info!(
1468                        %conn_id, %session_uuid, kind = "http_extended",
1469                        sql = %redact_sql_for_logging(query), num_params = params.len(),
1470                        "statement arrival"
1471                    );
1472                }
1473            }
1474        }
1475    }
1476
1477    // This API prohibits executing statements with responses whose
1478    // semantics are at odds with an HTTP response.
1479    fn check_prohibited_stmts<S: ResultSender>(
1480        sender: &S,
1481        stmt: &Statement<Raw>,
1482    ) -> Result<(), Error> {
1483        let kind: StatementKind = stmt.into();
1484        let execute_responses = Plan::generated_from(&kind)
1485            .into_iter()
1486            .map(ExecuteResponse::generated_from)
1487            .flatten()
1488            .collect::<Vec<_>>();
1489
1490        // Special-case `COPY TO` statements that are not `COPY ... TO STDOUT`, since
1491        // StatementKind::Copy links to several `ExecuteResponseKind`s that are not supported,
1492        // but this specific statement should be allowed.
1493        let is_valid_copy = matches!(
1494            stmt,
1495            Statement::Copy(CopyStatement {
1496                direction: CopyDirection::To,
1497                target: CopyTarget::Expr(_),
1498                ..
1499            }) | Statement::Copy(CopyStatement {
1500                direction: CopyDirection::From,
1501                target: CopyTarget::Expr(_),
1502                ..
1503            })
1504        );
1505
1506        if !is_valid_copy
1507            && execute_responses.iter().any(|execute_response| {
1508                // Returns true if a statement or execute response are unsupported.
1509                match execute_response {
1510                    ExecuteResponseKind::Subscribing if sender.allow_subscribe() => false,
1511                    ExecuteResponseKind::Fetch
1512                    | ExecuteResponseKind::Subscribing
1513                    | ExecuteResponseKind::CopyFrom
1514                    | ExecuteResponseKind::DeclaredCursor
1515                    | ExecuteResponseKind::ClosedCursor => true,
1516                    // Various statements generate `PeekPlan` (`SELECT`, `COPY`,
1517                    // `EXPLAIN`, `SHOW`) which has both `SendRows` and `CopyTo` as its
1518                    // possible response types. but `COPY` needs be picked out because
1519                    // http don't support its response type
1520                    ExecuteResponseKind::CopyTo if matches!(kind, StatementKind::Copy) => true,
1521                    _ => false,
1522                }
1523            })
1524        {
1525            return Err(Error::Unsupported(stmt.to_ast_string_simple()));
1526        }
1527        Ok(())
1528    }
1529
1530    fn parse<'a>(
1531        client: &SessionClient,
1532        query: &'a str,
1533    ) -> Result<Vec<StatementParseResult<'a>>, Error> {
1534        let result = client
1535            .parse(query)
1536            .map_err(|e| Error::Unstructured(anyhow!(e)))?;
1537        result.map_err(|e| AdapterError::from(e).into())
1538    }
1539
1540    let mut stmt_groups = vec![];
1541
1542    match request {
1543        SqlRequest::Simple { query } => match parse(client, query.as_str()) {
1544            Ok(stmts) => {
1545                let mut stmt_group = Vec::with_capacity(stmts.len());
1546                let mut stmt_err = None;
1547                for StatementParseResult { ast: stmt, sql } in stmts {
1548                    if let Err(err) = check_prohibited_stmts(sender, &stmt) {
1549                        stmt_err = Some(err);
1550                        break;
1551                    }
1552                    stmt_group.push((stmt, sql.to_string(), vec![]));
1553                }
1554                stmt_groups.push(stmt_err.map(Err).unwrap_or_else(|| Ok(stmt_group)));
1555            }
1556            Err(e) => stmt_groups.push(Err(e)),
1557        },
1558        SqlRequest::Extended { queries } => {
1559            for ExtendedRequest { query, params } in queries {
1560                match parse(client, &query) {
1561                    Ok(mut stmts) => {
1562                        if stmts.len() != 1 {
1563                            return Err(Error::Unstructured(anyhow!(
1564                                "each query must contain exactly 1 statement, but \"{}\" contains {}",
1565                                query,
1566                                stmts.len()
1567                            )));
1568                        }
1569
1570                        let StatementParseResult { ast: stmt, sql } = stmts.pop().unwrap();
1571                        stmt_groups.push(
1572                            check_prohibited_stmts(sender, &stmt)
1573                                .map(|_| vec![(stmt, sql.to_string(), params)]),
1574                        );
1575                    }
1576                    Err(e) => stmt_groups.push(Err(e)),
1577                };
1578            }
1579        }
1580    }
1581
1582    for stmt_group_res in stmt_groups {
1583        let executed = match stmt_group_res {
1584            Ok(stmt_group) => execute_stmt_group(client, sender, stmt_group).await,
1585            Err(e) => {
1586                let err = SqlResult::err(client, e);
1587                let _ = send_and_retire(err.into(), client, sender).await?;
1588                Ok(Err(()))
1589            }
1590        };
1591        // At the end of each group, commit implicit transactions. Do that here so that any `?`
1592        // early return can still be handled here.
1593        if client.session().transaction().is_implicit() {
1594            let ended = client.end_transaction(EndTransactionAction::Commit).await;
1595            if let Err(err) = ended {
1596                let err = SqlResult::err(client, err);
1597                let _ = send_and_retire(StatementResult::SqlResult(err), client, sender).await?;
1598            }
1599        }
1600        if executed?.is_err() {
1601            break;
1602        }
1603    }
1604
1605    Ok(())
1606}
1607
1608/// Executes a single statement in a [`SqlRequest`].
1609async fn execute_stmt<S: ResultSender>(
1610    client: &mut SessionClient,
1611    sender: &mut S,
1612    stmt: Statement<Raw>,
1613    sql: String,
1614    raw_params: Vec<Option<String>>,
1615) -> Result<StatementResult, Error> {
1616    const EMPTY_PORTAL: &str = "";
1617    if let Err(e) = client
1618        .prepare(EMPTY_PORTAL.into(), Some(stmt.clone()), sql, vec![])
1619        .await
1620    {
1621        return Ok(SqlResult::err(client, e).into());
1622    }
1623
1624    let prep_stmt = match client.get_prepared_statement(EMPTY_PORTAL).await {
1625        Ok(stmt) => stmt,
1626        Err(err) => {
1627            return Ok(SqlResult::err(client, err).into());
1628        }
1629    };
1630
1631    let param_types = &prep_stmt.desc().param_types;
1632    if param_types.len() != raw_params.len() {
1633        let message = anyhow!(
1634            "request supplied {actual} parameters, \
1635                        but {statement} requires {expected}",
1636            statement = stmt.to_ast_string_simple(),
1637            actual = raw_params.len(),
1638            expected = param_types.len()
1639        );
1640        return Ok(SqlResult::err(client, Error::Unstructured(message)).into());
1641    }
1642
1643    let buf = RowArena::new();
1644    let mut params = vec![];
1645    for (raw_param, mz_typ) in raw_params.into_iter().zip_eq(param_types) {
1646        let pg_typ = mz_pgrepr::Type::from(mz_typ);
1647        let datum = match raw_param {
1648            None => Datum::Null,
1649            Some(raw_param) => {
1650                match mz_pgrepr::Value::decode(
1651                    mz_pgwire_common::Format::Text,
1652                    &pg_typ,
1653                    raw_param.as_bytes(),
1654                ) {
1655                    Ok(param) => match param.into_datum_decode_error(&buf, &pg_typ, "parameter") {
1656                        Ok(datum) => datum,
1657                        Err(msg) => {
1658                            return Ok(
1659                                SqlResult::err(client, Error::Unstructured(anyhow!(msg))).into()
1660                            );
1661                        }
1662                    },
1663                    Err(err) => {
1664                        let msg = anyhow!("unable to decode parameter: {}", err);
1665                        return Ok(SqlResult::err(client, Error::Unstructured(msg)).into());
1666                    }
1667                }
1668            }
1669        };
1670        params.push((datum, mz_typ.clone()))
1671    }
1672
1673    let result_formats = vec![
1674        mz_pgwire_common::Format::Text;
1675        prep_stmt
1676            .desc()
1677            .relation_desc
1678            .clone()
1679            .map(|desc| desc.typ().column_types.len())
1680            .unwrap_or(0)
1681    ];
1682
1683    let desc = prep_stmt.desc().clone();
1684    let logging = Arc::clone(prep_stmt.logging());
1685    let stmt_ast = prep_stmt.stmt().cloned();
1686    let state_revision = prep_stmt.state_revision;
1687    if let Err(err) = client.session().set_portal(
1688        EMPTY_PORTAL.into(),
1689        desc,
1690        stmt_ast,
1691        logging,
1692        params,
1693        result_formats,
1694        state_revision,
1695    ) {
1696        return Ok(SqlResult::err(client, err).into());
1697    }
1698
1699    let desc = client
1700        .session()
1701        // We do not need to verify here because `client.execute` verifies below.
1702        .get_portal_unverified(EMPTY_PORTAL)
1703        .map(|portal| portal.desc.clone())
1704        .expect("unnamed portal should be present");
1705
1706    let res = client
1707        .execute(EMPTY_PORTAL.into(), futures::future::pending(), None)
1708        .await;
1709
1710    if S::SUPPORTS_STREAMING_NOTICES {
1711        sender
1712            .emit_streaming_notices(client.session().drain_notices())
1713            .await?;
1714    }
1715
1716    let (res, execute_started) = match res {
1717        Ok(res) => res,
1718        Err(e) => {
1719            return Ok(SqlResult::err(client, e).into());
1720        }
1721    };
1722    let tag = res.tag();
1723
1724    Ok(match res {
1725        ExecuteResponse::CreatedConnection { .. }
1726        | ExecuteResponse::CreatedDatabase { .. }
1727        | ExecuteResponse::CreatedSchema { .. }
1728        | ExecuteResponse::CreatedRole
1729        | ExecuteResponse::CreatedCluster { .. }
1730        | ExecuteResponse::CreatedClusterReplica { .. }
1731        | ExecuteResponse::CreatedTable { .. }
1732        | ExecuteResponse::CreatedIndex { .. }
1733        | ExecuteResponse::CreatedMetricSink { .. }
1734        | ExecuteResponse::CreatedIntrospectionSubscribe
1735        | ExecuteResponse::CreatedSecret { .. }
1736        | ExecuteResponse::CreatedSource { .. }
1737        | ExecuteResponse::CreatedSink { .. }
1738        | ExecuteResponse::CreatedView { .. }
1739        | ExecuteResponse::CreatedViews { .. }
1740        | ExecuteResponse::CreatedMaterializedView { .. }
1741        | ExecuteResponse::CreatedType
1742        | ExecuteResponse::CreatedNetworkPolicy
1743        | ExecuteResponse::Comment
1744        | ExecuteResponse::Deleted(_)
1745        | ExecuteResponse::DiscardedTemp
1746        | ExecuteResponse::DroppedObject(_)
1747        | ExecuteResponse::DroppedOwned
1748        | ExecuteResponse::EmptyQuery
1749        | ExecuteResponse::GrantedPrivilege
1750        | ExecuteResponse::GrantedRole
1751        | ExecuteResponse::Inserted(_)
1752        | ExecuteResponse::Copied(_)
1753        | ExecuteResponse::Raised
1754        | ExecuteResponse::ReassignOwned
1755        | ExecuteResponse::RevokedPrivilege
1756        | ExecuteResponse::AlteredDefaultPrivileges
1757        | ExecuteResponse::RevokedRole
1758        | ExecuteResponse::StartedTransaction { .. }
1759        | ExecuteResponse::Updated(_)
1760        | ExecuteResponse::AlteredObject(_)
1761        | ExecuteResponse::AlteredRole
1762        | ExecuteResponse::AlteredSystemConfiguration
1763        | ExecuteResponse::Deallocate { .. }
1764        | ExecuteResponse::ValidatedConnection
1765        | ExecuteResponse::Prepare => SqlResult::ok(
1766            client,
1767            tag.expect("ok only called on tag-generating results"),
1768            Vec::default(),
1769        )
1770        .into(),
1771        ExecuteResponse::TransactionCommitted { params }
1772        | ExecuteResponse::TransactionRolledBack { params }
1773        | ExecuteResponse::DiscardedAll { params } => {
1774            let notify_set: mz_ore::collections::HashSet<_> = client
1775                .session()
1776                .vars()
1777                .notify_set()
1778                .map(|v| v.name().to_string())
1779                .collect();
1780            let params = params
1781                .into_iter()
1782                .filter(|(name, _value)| notify_set.contains(*name))
1783                .map(|(name, value)| ParameterStatus {
1784                    name: name.to_string(),
1785                    value,
1786                })
1787                .collect();
1788            SqlResult::ok(
1789                client,
1790                tag.expect("ok only called on tag-generating results"),
1791                params,
1792            )
1793            .into()
1794        }
1795        ExecuteResponse::SetVariable { name, .. } => {
1796            let mut params = Vec::with_capacity(1);
1797            if let Some(var) = client
1798                .session()
1799                .vars()
1800                .notify_set()
1801                .find(|v| v.name() == &name)
1802            {
1803                params.push(ParameterStatus {
1804                    name,
1805                    value: var.value(),
1806                });
1807            };
1808            SqlResult::ok(
1809                client,
1810                tag.expect("ok only called on tag-generating results"),
1811                params,
1812            )
1813            .into()
1814        }
1815        ExecuteResponse::SendingRowsStreaming {
1816            rows,
1817            instance_id,
1818            strategy,
1819        } => {
1820            let max_result_size =
1821                usize::cast_from(client.get_system_vars().await.max_result_size());
1822
1823            let rows_stream = RecordFirstRowStream::new(
1824                Box::new(rows),
1825                execute_started,
1826                client,
1827                Some(instance_id),
1828                Some(strategy),
1829            );
1830
1831            StatementResult::Rows {
1832                desc: desc.relation_desc.expect("RelationDesc must exist"),
1833                rows_stream,
1834                max_result_size,
1835            }
1836        }
1837        ExecuteResponse::SendingRowsImmediate { rows } => {
1838            let max_result_size =
1839                usize::cast_from(client.get_system_vars().await.max_result_size());
1840
1841            let rows = futures::stream::once(futures::future::ready(PeekResponseUnary::Rows(rows)));
1842            let rows_stream =
1843                RecordFirstRowStream::new(Box::new(rows), execute_started, client, None, None);
1844
1845            StatementResult::Rows {
1846                desc: desc.relation_desc.expect("RelationDesc must exist"),
1847                rows_stream,
1848                max_result_size,
1849            }
1850        }
1851        ExecuteResponse::Subscribing {
1852            rx,
1853            ctx_extra,
1854            instance_id,
1855        } => StatementResult::Subscribe {
1856            tag: "SUBSCRIBE".into(),
1857            desc: desc.relation_desc.unwrap(),
1858            rx: RecordFirstRowStream::new(rx, execute_started, client, Some(instance_id), None),
1859            ctx_extra,
1860        },
1861        res @ (ExecuteResponse::Fetch { .. }
1862        | ExecuteResponse::CopyTo { .. }
1863        | ExecuteResponse::CopyFrom { .. }
1864        | ExecuteResponse::DeclaredCursor
1865        | ExecuteResponse::ClosedCursor) => SqlResult::err(
1866            client,
1867            Error::Unstructured(anyhow!(
1868                "internal error: encountered prohibited ExecuteResponse {:?}.\n\n
1869            This is a bug. Can you please file an bug report letting us know?\n
1870            https://github.com/MaterializeInc/materialize/discussions/new?category=bug-reports",
1871                ExecuteResponseKind::from(res)
1872            )),
1873        )
1874        .into(),
1875    })
1876}
1877
1878fn make_notices(client: &mut SessionClient) -> Vec<Notice> {
1879    client
1880        .session()
1881        .drain_notices()
1882        .into_iter()
1883        .map(|notice| Notice {
1884            message: notice.to_string(),
1885            code: notice.code().code().to_string(),
1886            severity: notice.severity().as_str().to_lowercase(),
1887            detail: notice.detail(),
1888            hint: notice.hint(),
1889        })
1890        .collect()
1891}
1892
1893// Duplicated from protocol.rs.
1894// See postgres' backend/tcop/postgres.c IsTransactionExitStmt.
1895fn is_txn_exit_stmt(stmt: &Statement<Raw>) -> bool {
1896    matches!(
1897        stmt,
1898        Statement::Commit(_) | Statement::Rollback(_) | Statement::Prepare(_)
1899    )
1900}
1901
1902#[cfg(test)]
1903mod tests {
1904    use std::collections::BTreeMap;
1905
1906    use super::{Password, WebSocketAuth};
1907
1908    #[mz_ore::test]
1909    fn smoke_test_websocket_auth_parse() {
1910        struct TestCase {
1911            json: &'static str,
1912            expected: WebSocketAuth,
1913        }
1914
1915        let test_cases = vec![
1916            TestCase {
1917                json: r#"{ "user": "mz", "password": "1234" }"#,
1918                expected: WebSocketAuth::Basic {
1919                    user: "mz".to_string(),
1920                    password: Password("1234".to_string()),
1921                    options: BTreeMap::default(),
1922                },
1923            },
1924            TestCase {
1925                json: r#"{ "user": "mz", "password": "1234", "options": {} }"#,
1926                expected: WebSocketAuth::Basic {
1927                    user: "mz".to_string(),
1928                    password: Password("1234".to_string()),
1929                    options: BTreeMap::default(),
1930                },
1931            },
1932            TestCase {
1933                json: r#"{ "token": "i_am_a_token" }"#,
1934                expected: WebSocketAuth::Bearer {
1935                    token: "i_am_a_token".to_string(),
1936                    options: BTreeMap::default(),
1937                },
1938            },
1939            TestCase {
1940                json: r#"{ "token": "i_am_a_token", "options": { "foo": "bar" } }"#,
1941                expected: WebSocketAuth::Bearer {
1942                    token: "i_am_a_token".to_string(),
1943                    options: BTreeMap::from([("foo".to_string(), "bar".to_string())]),
1944                },
1945            },
1946        ];
1947
1948        fn assert_parse(json: &'static str, expected: WebSocketAuth) {
1949            let parsed: WebSocketAuth = serde_json::from_str(json).unwrap();
1950            assert_eq!(parsed, expected);
1951        }
1952
1953        for TestCase { json, expected } in test_cases {
1954            assert_parse(json, expected)
1955        }
1956    }
1957}