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