1use itertools::Itertools;
14use mz_adapter_types::connection::ConnectionId;
15use mz_ore::now::EpochMillis;
16use mz_repr::{Diff, GlobalId, SqlScalarType};
17use mz_sql::names::{Aug, ResolvedIds};
18use mz_sql::plan::{Params, StatementDesc};
19use mz_sql::session::metadata::SessionMetadata;
20use mz_sql_parser::ast::{Raw, Statement};
21
22use crate::active_compute_sink::{ActiveComputeSink, ActiveComputeSinkRetireReason};
23use crate::catalog::Catalog;
24use crate::coord::appends::{BuiltinTableAppendCompletion, BuiltinTableAppendNotify};
25use crate::coord::{Coordinator, Message};
26use crate::session::{Session, StateRevision, TransactionStatus};
27use crate::util::describe;
28use crate::{AdapterError, ExecuteContext, ExecuteResponse, metrics};
29
30impl Coordinator {
31 pub(crate) fn plan_statement(
35 &self,
36 session: &Session,
37 stmt: mz_sql::ast::Statement<Aug>,
38 params: &mz_sql::plan::Params,
39 resolved_ids: &ResolvedIds,
40 ) -> Result<(mz_sql::plan::Plan, ResolvedIds), AdapterError> {
41 let pcx = session.pcx();
42 let catalog = self.catalog().for_session(session);
43 let (plan, sql_impl_ids) =
44 mz_sql::plan::plan(Some(pcx), &catalog, stmt, params, resolved_ids)?;
45 Ok((plan, sql_impl_ids))
46 }
47
48 pub(crate) fn declare(
49 &self,
50 mut ctx: ExecuteContext,
51 name: String,
52 stmt: Statement<Raw>,
53 sql: String,
54 params: Params,
55 ) {
56 let catalog = self.owned_catalog();
57 let now = self.now();
58 mz_ore::task::spawn(|| "coord::declare", async move {
59 let result =
60 Self::declare_inner(ctx.session_mut(), &catalog, name, stmt, sql, params, now)
61 .map(|()| ExecuteResponse::DeclaredCursor);
62 ctx.retire(result);
63 });
64 }
65
66 fn declare_inner(
67 session: &mut Session,
68 catalog: &Catalog,
69 name: String,
70 stmt: Statement<Raw>,
71 sql: String,
72 params: Params,
73 now: EpochMillis,
74 ) -> Result<(), AdapterError> {
75 let param_types = params
76 .execute_types
77 .iter()
78 .map(|ty| Some(ty.clone()))
79 .collect::<Vec<_>>();
80 let desc = describe(catalog, stmt.clone(), ¶m_types, session)?;
81 let params = params
82 .datums
83 .into_iter()
84 .zip_eq(params.execute_types)
85 .collect();
86 let result_formats = vec![mz_pgwire_common::Format::Text; desc.arity()];
87 let logging = session.mint_logging(sql, Some(&stmt), now);
88 let state_revision = StateRevision {
89 catalog_revision: catalog.transient_revision(),
90 session_state_revision: session.state_revision(),
91 };
92 session.set_portal(
93 name,
94 desc,
95 Some(stmt),
96 logging,
97 params,
98 result_formats,
99 state_revision,
100 )?;
101 Ok(())
102 }
103
104 #[mz_ore::instrument(level = "debug")]
105 pub(crate) fn describe(
106 catalog: &Catalog,
107 session: &Session,
108 stmt: Option<Statement<Raw>>,
109 param_types: Vec<Option<SqlScalarType>>,
110 ) -> Result<StatementDesc, AdapterError> {
111 if let Some(stmt) = stmt {
112 describe(catalog, stmt, ¶m_types, session)
113 } else {
114 Ok(StatementDesc::new(None))
115 }
116 }
117
118 pub(crate) fn verify_prepared_statement(
122 catalog: &Catalog,
123 session: &mut Session,
124 name: &str,
125 ) -> Result<(), AdapterError> {
126 let ps = match session.get_prepared_statement_unverified(name) {
127 Some(ps) => ps,
128 None => return Err(AdapterError::UnknownPreparedStatement(name.to_string())),
129 };
130 if let Some(new_revision) = Self::verify_statement_revision(
131 catalog,
132 session,
133 ps.stmt(),
134 ps.desc(),
135 ps.state_revision,
136 )? {
137 let ps = session
138 .get_prepared_statement_mut_unverified(name)
139 .expect("known to exist");
140 ps.state_revision = new_revision;
141 }
142
143 Ok(())
144 }
145
146 pub(crate) fn verify_portal(
148 catalog: &Catalog,
149 session: &mut Session,
150 name: &str,
151 ) -> Result<(), AdapterError> {
152 let portal = match session.get_portal_unverified(name) {
153 Some(portal) => portal,
154 None => return Err(AdapterError::UnknownCursor(name.to_string())),
155 };
156 if let Some(new_revision) = Self::verify_statement_revision(
157 catalog,
158 session,
159 portal.stmt.as_deref(),
160 &portal.desc,
161 portal.state_revision,
162 )? {
163 let portal = session
164 .get_portal_unverified_mut(name)
165 .expect("known to exist");
166 *portal.state_revision = new_revision;
167 }
168 Ok(())
169 }
170
171 fn verify_statement_revision(
176 catalog: &Catalog,
177 session: &Session,
178 stmt: Option<&Statement<Raw>>,
179 desc: &StatementDesc,
180 old_state_revision: StateRevision,
181 ) -> Result<Option<StateRevision>, AdapterError> {
182 let current_state_revision = StateRevision {
183 catalog_revision: catalog.transient_revision(),
184 session_state_revision: session.state_revision(),
185 };
186 if old_state_revision != current_state_revision {
187 let current_desc = Self::describe(
188 catalog,
189 session,
190 stmt.cloned(),
191 desc.param_types.iter().map(|ty| Some(ty.clone())).collect(),
192 )?;
193 if ¤t_desc != desc {
194 Err(AdapterError::ChangedPlan(
195 "cached plan must not change result type".to_string(),
196 ))
197 } else {
198 Ok(Some(current_state_revision))
199 }
200 } else {
201 Ok(None)
202 }
203 }
204
205 pub(crate) async fn clear_transaction(
211 &mut self,
212 session: &mut Session,
213 ) -> (TransactionStatus, BuiltinTableAppendCompletion) {
214 let retire_notify = self.clear_connection(session.conn_id()).await;
221 (session.clear_transaction(), retire_notify)
222 }
223
224 pub(crate) async fn clear_connection(
229 &mut self,
230 conn_id: &ConnectionId,
231 ) -> BuiltinTableAppendCompletion {
232 self.connection_cancel_watches.remove(conn_id);
233 let retire_notify = self
234 .retire_compute_sinks_for_conn(conn_id, ActiveComputeSinkRetireReason::Finished)
235 .await;
236 self.retire_cluster_reconfigurations_for_conn(conn_id).await;
237
238 if let Some(txn_reads) = self.txn_read_holds.remove(conn_id) {
240 tracing::debug!(?txn_reads, "releasing txn read holds");
241
242 drop(txn_reads);
245 }
246
247 if let Some(_guard) = self
248 .active_conns
249 .get_mut(conn_id)
250 .expect("must exist for active session")
251 .deferred_lock
252 .take()
253 {
254 if !self.serialized_ddl.is_empty() {
256 let _ = self.internal_cmd_tx.send(Message::DeferredStatementReady);
257 }
258 }
259
260 retire_notify
261 }
262
263 pub(crate) fn add_active_compute_sink(
268 &mut self,
269 id: GlobalId,
270 active_sink: ActiveComputeSink,
271 ) -> BuiltinTableAppendNotify {
272 let user = self.active_conns()[active_sink.connection_id()].user();
273 let session_type = metrics::session_type_label_value(user);
274
275 self.active_conns
276 .get_mut(active_sink.connection_id())
277 .expect("must exist for active sessions")
278 .drop_sinks
279 .insert(id);
280
281 let ret_fut: BuiltinTableAppendNotify = match &active_sink {
282 ActiveComputeSink::Subscribe(active_subscribe) => {
283 let table_update_fut = if !active_subscribe.internal {
284 let update = self.catalog().state().pack_subscribe_update(
285 id,
286 active_subscribe,
287 Diff::ONE,
288 );
289 let update = self.catalog().state().resolve_builtin_table_update(update);
290
291 self.builtin_table_update().defer(vec![update])
297 } else {
298 Box::pin(std::future::ready(()))
300 };
301
302 self.metrics
303 .active_subscribes
304 .with_label_values(&[session_type])
305 .inc();
306
307 table_update_fut
308 }
309 ActiveComputeSink::CopyTo(_) => {
310 self.metrics
311 .active_copy_tos
312 .with_label_values(&[session_type])
313 .inc();
314 Box::pin(std::future::ready(()))
315 }
316 };
317 self.active_compute_sinks.insert(id, active_sink);
318 ret_fut
319 }
320
321 #[mz_ore::instrument(level = "debug")]
334 pub(crate) async fn remove_active_compute_sink(
335 &mut self,
336 id: GlobalId,
337 ) -> Option<(ActiveComputeSink, BuiltinTableAppendNotify)> {
338 if let Some(sink) = self.active_compute_sinks.remove(&id) {
339 let user = self.active_conns()[sink.connection_id()].user();
340 let session_type = metrics::session_type_label_value(user);
341
342 self.active_conns
343 .get_mut(sink.connection_id())
344 .expect("must exist for active compute sink")
345 .drop_sinks
346 .remove(&id);
347
348 let write_notify: BuiltinTableAppendNotify = match &sink {
349 ActiveComputeSink::Subscribe(active_subscribe) => {
350 let notify = if !active_subscribe.internal {
352 let update = self.catalog().state().pack_subscribe_update(
353 id,
354 active_subscribe,
355 Diff::MINUS_ONE,
356 );
357 let update = self.catalog().state().resolve_builtin_table_update(update);
358 self.builtin_table_update().defer(vec![update])
363 } else {
364 Box::pin(std::future::ready(()))
365 };
366
367 self.metrics
368 .active_subscribes
369 .with_label_values(&[session_type])
370 .dec();
371
372 notify
373 }
374 ActiveComputeSink::CopyTo(_) => {
375 self.metrics
376 .active_copy_tos
377 .with_label_values(&[session_type])
378 .dec();
379
380 Box::pin(std::future::ready(()))
381 }
382 };
383 Some((sink, write_notify))
384 } else {
385 None
386 }
387 }
388}