1use std::collections::{BTreeMap, BTreeSet};
11use std::fmt::Debug;
12
13use itertools::Itertools;
14use mz_catalog::durable::{DurableCatalogError, FenceError};
15use mz_compute_client::controller::error::{
16 CollectionUpdateError, DataflowCreationError, InstanceMissing, PeekError, ReadPolicyError,
17};
18use mz_controller_types::ClusterId;
19use mz_ore::tracing::OpenTelemetryContext;
20use mz_ore::{assert_none, exit, soft_assert_no_log};
21use mz_repr::{RelationDesc, RowIterator, SqlScalarType};
22use mz_sql::names::FullItemName;
23use mz_sql::plan::StatementDesc;
24use mz_sql::session::metadata::SessionMetadata;
25use mz_sql::session::vars::Var;
26use mz_sql_parser::ast::display::AstDisplay;
27use mz_sql_parser::ast::{
28 CreateIndexStatement, Ident, Raw, RawClusterName, RawItemName, Statement,
29};
30use mz_storage_types::controller::StorageError;
31use mz_transform::TransformError;
32use tokio::sync::mpsc::UnboundedSender;
33use tokio::sync::oneshot;
34
35use crate::catalog::{Catalog, CatalogState};
36use crate::command::{Command, Response};
37use crate::coord::{Message, PendingTxnResponse};
38use crate::error::AdapterError;
39use crate::session::{EndTransactionAction, Session};
40use crate::{ExecuteContext, ExecuteResponse};
41
42#[derive(Debug)]
44pub struct ClientTransmitter<T>
45where
46 T: Transmittable,
47 <T as Transmittable>::Allowed: 'static,
48{
49 tx: Option<oneshot::Sender<Response<T>>>,
50 internal_cmd_tx: UnboundedSender<Message>,
51 allowed: Option<&'static [T::Allowed]>,
54}
55
56impl<T: Transmittable + std::fmt::Debug> ClientTransmitter<T> {
57 pub fn new(
59 tx: oneshot::Sender<Response<T>>,
60 internal_cmd_tx: UnboundedSender<Message>,
61 ) -> ClientTransmitter<T> {
62 ClientTransmitter {
63 tx: Some(tx),
64 internal_cmd_tx,
65 allowed: None,
66 }
67 }
68
69 #[mz_ore::instrument(level = "debug")]
76 pub fn send(mut self, result: Result<T, AdapterError>, session: Session) {
77 soft_assert_no_log!(
79 match (&result, self.allowed.take()) {
80 (Ok(t), Some(allowed)) => allowed.contains(&t.to_allowed()),
81 _ => true,
82 },
83 "tried to send disallowed value {result:?} through ClientTransmitter; \
84 see ClientTransmitter::set_allowed"
85 );
86
87 if let Err(res) = self
90 .tx
91 .take()
92 .expect("tx will always be `Some` unless `self` has been consumed")
93 .send(Response {
94 result,
95 session,
96 otel_ctx: OpenTelemetryContext::obtain(),
97 })
98 {
99 let send_res = self.internal_cmd_tx.send(Message::Command(
102 OpenTelemetryContext::obtain(),
103 Command::Terminate {
104 conn_id: res.session.conn_id().clone(),
105 tx: None,
106 },
107 ));
108 if send_res.is_err() {
109 tracing::warn!("coordinator gone, could not clean up session");
110 }
111 }
112 }
113
114 pub fn take(mut self) -> oneshot::Sender<Response<T>> {
115 self.tx
116 .take()
117 .expect("tx will always be `Some` unless `self` has been consumed")
118 }
119
120 pub fn set_allowed(&mut self, allowed: &'static [T::Allowed]) {
124 self.allowed = Some(allowed);
125 }
126}
127
128pub trait Transmittable {
130 type Allowed: Eq + PartialEq + std::fmt::Debug;
132 fn to_allowed(&self) -> Self::Allowed;
142}
143
144impl Transmittable for () {
145 type Allowed = bool;
146
147 fn to_allowed(&self) -> Self::Allowed {
148 true
149 }
150}
151
152#[derive(Debug)]
156pub struct CompletedClientTransmitter {
157 ctx: ExecuteContext,
158 response: Result<PendingTxnResponse, AdapterError>,
159 action: EndTransactionAction,
160}
161
162impl CompletedClientTransmitter {
163 pub fn new(
165 ctx: ExecuteContext,
166 response: Result<PendingTxnResponse, AdapterError>,
167 action: EndTransactionAction,
168 ) -> Self {
169 CompletedClientTransmitter {
170 ctx,
171 response,
172 action,
173 }
174 }
175
176 pub fn finalize(mut self) -> (ExecuteContext, Result<ExecuteResponse, AdapterError>) {
178 let changed = self
179 .ctx
180 .session_mut()
181 .vars_mut()
182 .end_transaction(self.action);
183
184 let response = self.response.map(|mut r| {
186 r.extend_params(changed);
187 ExecuteResponse::from(r)
188 });
189
190 (self.ctx, response)
191 }
192}
193
194impl<T: Transmittable> Drop for ClientTransmitter<T> {
195 fn drop(&mut self) {
196 if self.tx.is_some() {
197 panic!("client transmitter dropped without send")
198 }
199 }
200}
201
202pub fn index_sql(
205 index_name: String,
206 cluster_id: ClusterId,
207 view_name: FullItemName,
208 view_desc: &RelationDesc,
209 keys: &[usize],
210) -> String {
211 use mz_sql::ast::{Expr, Value};
212
213 CreateIndexStatement::<Raw> {
214 name: Some(Ident::new_unchecked(index_name)),
215 on_name: RawItemName::Name(mz_sql::normalize::unresolve(view_name)),
216 in_cluster: Some(RawClusterName::Resolved(cluster_id.to_string())),
217 key_parts: Some(
218 keys.iter()
219 .map(|i| match view_desc.get_unambiguous_name(*i) {
220 Some(n) => Expr::Identifier(vec![Ident::new_unchecked(n.to_string())]),
221 _ => Expr::Value(Value::Number((i + 1).to_string())),
222 })
223 .collect(),
224 ),
225 with_options: vec![],
226 if_not_exists: false,
227 }
228 .to_ast_string_stable()
229}
230
231pub fn describe(
233 catalog: &Catalog,
234 stmt: Statement<Raw>,
235 param_types: &[Option<SqlScalarType>],
236 session: &Session,
237) -> Result<StatementDesc, AdapterError> {
238 let catalog = &catalog.for_session(session);
239 let (stmt, _) = mz_sql::names::resolve(catalog, stmt)?;
240 Ok(mz_sql::plan::describe(
241 session.pcx(),
242 catalog,
243 stmt,
244 param_types,
245 )?)
246}
247
248pub trait ResultExt<T> {
249 fn unwrap_or_terminate(self, context: &str) -> T;
253
254 fn maybe_terminate(self, context: &str) -> Self;
258}
259
260impl<T, E> ResultExt<T> for Result<T, E>
261where
262 E: ShouldTerminateGracefully + Debug,
263{
264 fn unwrap_or_terminate(self, context: &str) -> T {
265 match self {
266 Ok(t) => t,
267 Err(e) if e.should_terminate_gracefully() => exit!(0, "{context}: {e:?}"),
268 Err(e) => panic!("{context}: {e:?}"),
269 }
270 }
271
272 fn maybe_terminate(self, context: &str) -> Self {
273 if let Err(e) = &self {
274 if e.should_terminate_gracefully() {
275 exit!(0, "{context}: {e:?}");
276 }
277 }
278
279 self
280 }
281}
282
283trait ShouldTerminateGracefully {
286 fn should_terminate_gracefully(&self) -> bool;
289}
290
291impl ShouldTerminateGracefully for AdapterError {
292 fn should_terminate_gracefully(&self) -> bool {
293 match self {
294 AdapterError::Catalog(e) => e.should_terminate_gracefully(),
295 _ => false,
296 }
297 }
298}
299
300impl ShouldTerminateGracefully for mz_catalog::memory::error::Error {
301 fn should_terminate_gracefully(&self) -> bool {
302 match &self.kind {
303 mz_catalog::memory::error::ErrorKind::Durable(e) => e.should_terminate_gracefully(),
304 _ => false,
305 }
306 }
307}
308
309impl ShouldTerminateGracefully for mz_catalog::durable::CatalogError {
310 fn should_terminate_gracefully(&self) -> bool {
311 match &self {
312 Self::Durable(e) => e.should_terminate_gracefully(),
313 Self::Catalog(_) => false,
314 }
315 }
316}
317
318impl ShouldTerminateGracefully for DurableCatalogError {
319 fn should_terminate_gracefully(&self) -> bool {
320 match self {
321 DurableCatalogError::Fence(err) => err.should_terminate_gracefully(),
322 DurableCatalogError::CatalogOutOfSync { .. } => true,
323 DurableCatalogError::IncompatibleDataVersion { .. }
324 | DurableCatalogError::IncompatiblePersistVersion { .. }
325 | DurableCatalogError::Proto(_)
326 | DurableCatalogError::Uninitialized
327 | DurableCatalogError::NotWritable(_)
328 | DurableCatalogError::DryRunTransaction
329 | DurableCatalogError::DuplicateKey
330 | DurableCatalogError::UniquenessViolation
331 | DurableCatalogError::Storage(_)
332 | DurableCatalogError::Internal(_) => false,
333 }
334 }
335}
336
337impl ShouldTerminateGracefully for FenceError {
338 fn should_terminate_gracefully(&self) -> bool {
339 match self {
340 FenceError::DeployGeneration { .. } => true,
341 FenceError::Epoch { .. } | FenceError::MigrationUpper { .. } => false,
342 }
343 }
344}
345
346impl ShouldTerminateGracefully for StorageError {
347 fn should_terminate_gracefully(&self) -> bool {
348 match self {
349 StorageError::CollectionMetadataAlreadyExists(_)
350 | StorageError::PersistShardAlreadyInUse(_)
351 | StorageError::PersistSchemaEvolveRace { .. }
352 | StorageError::PersistInvalidSchemaEvolve { .. }
353 | StorageError::TxnWalShardAlreadyExists
354 | StorageError::UpdateBeyondUpper(_)
355 | StorageError::ReadBeforeSince(_)
356 | StorageError::InvalidUppers(_)
357 | StorageError::InvalidUsage(_)
358 | StorageError::CollectionIdReused(_)
359 | StorageError::SinkIdReused(_)
360 | StorageError::IdentifierMissing(_)
361 | StorageError::IdentifierInvalid(_)
362 | StorageError::IngestionInstanceMissing { .. }
363 | StorageError::ExportInstanceMissing { .. }
364 | StorageError::Generic(_)
365 | StorageError::ReadOnly
366 | StorageError::DataflowError(_)
367 | StorageError::InvalidAlter { .. }
368 | StorageError::ShuttingDown(_)
369 | StorageError::MissingSubsourceReference { .. }
370 | StorageError::RtrTimeout(_)
371 | StorageError::RtrDropFailure(_) => false,
372 }
373 }
374}
375
376impl ShouldTerminateGracefully for DataflowCreationError {
377 fn should_terminate_gracefully(&self) -> bool {
378 match self {
379 DataflowCreationError::SinceViolation(_)
380 | DataflowCreationError::InstanceMissing(_)
381 | DataflowCreationError::CollectionMissing(_)
382 | DataflowCreationError::ReplicaMissing(_)
383 | DataflowCreationError::MissingAsOf
384 | DataflowCreationError::EmptyAsOfForSubscribe
385 | DataflowCreationError::EmptyAsOfForCopyTo => false,
386 }
387 }
388}
389
390impl ShouldTerminateGracefully for CollectionUpdateError {
391 fn should_terminate_gracefully(&self) -> bool {
392 match self {
393 CollectionUpdateError::InstanceMissing(_)
394 | CollectionUpdateError::CollectionMissing(_) => false,
395 }
396 }
397}
398
399impl ShouldTerminateGracefully for PeekError {
400 fn should_terminate_gracefully(&self) -> bool {
401 match self {
402 PeekError::SinceViolation(_)
403 | PeekError::ReadHoldIdMismatch(_)
404 | PeekError::InstanceMissing(_)
405 | PeekError::CollectionMissing(_)
406 | PeekError::ReplicaMissing(_) => false,
407 }
408 }
409}
410
411impl ShouldTerminateGracefully for ReadPolicyError {
412 fn should_terminate_gracefully(&self) -> bool {
413 match self {
414 ReadPolicyError::InstanceMissing(_)
415 | ReadPolicyError::CollectionMissing(_)
416 | ReadPolicyError::WriteOnlyCollection(_) => false,
417 }
418 }
419}
420
421impl ShouldTerminateGracefully for TransformError {
422 fn should_terminate_gracefully(&self) -> bool {
423 match self {
424 TransformError::Internal(_)
425 | TransformError::IdentifierMissing(_)
426 | TransformError::CallerShouldPanic(_) => false,
427 }
428 }
429}
430
431impl ShouldTerminateGracefully for InstanceMissing {
432 fn should_terminate_gracefully(&self) -> bool {
433 false
434 }
435}
436
437pub(crate) fn viewable_variables<'a>(
439 catalog: &'a CatalogState,
440 session: &'a dyn SessionMetadata,
441) -> impl Iterator<Item = &'a dyn Var> {
442 session
443 .vars()
444 .iter()
445 .chain(catalog.system_config().iter())
446 .filter(|v| v.visible(session.user(), catalog.system_config()).is_ok())
447}
448
449pub fn verify_datum_desc(
451 desc: &RelationDesc,
452 rows: &mut dyn RowIterator,
453) -> Result<(), AdapterError> {
454 let Some(row) = rows.peek() else {
461 return Ok(());
462 };
463
464 let datums = row.unpack();
465 let col_types = &desc.typ().column_types;
466 if datums.len() != col_types.len() {
467 let msg = format!(
468 "internal error: row descriptor has {} columns but row has {} columns",
469 col_types.len(),
470 datums.len(),
471 );
472 return Err(AdapterError::Internal(msg));
473 }
474
475 for (i, (d, t)) in datums.iter().zip_eq(col_types).enumerate() {
476 if !d.is_instance_of_sql(t) {
477 let msg = format!(
478 "internal error: column {} is not of expected type {:?}: {:?}",
479 i, t, d
480 );
481 return Err(AdapterError::Internal(msg));
482 }
483 }
484
485 Ok(())
486}
487
488pub fn sort_topological<T, K, FK, FD>(items: &mut Vec<T>, key_fn: FK, dependencies_fn: FD)
495where
496 T: Debug,
497 K: Debug + Copy + Ord,
498 FK: Fn(&T) -> K,
499 FD: Fn(&T) -> BTreeSet<K>,
500{
501 let mut items_by_key = BTreeMap::new();
502 for item in items.drain(..) {
503 let key = key_fn(&item);
504 let prev = items_by_key.insert(key, item);
505 assert_none!(prev);
506 }
507
508 let mut in_degree = BTreeMap::<K, usize>::new();
510 let mut dependents = BTreeMap::<K, Vec<K>>::new();
512 let mut ready = Vec::<K>::new();
514
515 for (&key, item) in &items_by_key {
517 let mut dependencies = dependencies_fn(item);
518 dependencies.retain(|dep| items_by_key.contains_key(dep) && *dep != key);
520
521 in_degree.insert(key, dependencies.len());
522
523 for dep in &dependencies {
524 dependents.entry(*dep).or_default().push(key);
525 }
526
527 if dependencies.is_empty() {
528 ready.push(key);
529 }
530 }
531
532 while let Some(id) = ready.pop() {
534 let item = items_by_key.remove(&id).expect("must exist");
535 items.push(item);
536
537 if let Some(depts) = dependents.get(&id) {
538 for dept in depts {
539 let deg = in_degree.get_mut(dept).expect("must exist");
540 *deg -= 1;
541 if *deg == 0 {
542 ready.push(*dept);
543 }
544 }
545 }
546 }
547
548 if !items_by_key.is_empty() {
550 panic!("dependency cycle: {items_by_key:?}");
551 }
552}