1mod notice;
11
12use bytesize::ByteSize;
13use ipnet::IpNet;
14use mz_adapter_types::compaction::CompactionWindow;
15use mz_audit_log::VersionedStorageUsage;
16use mz_catalog::SYSTEM_CONN_ID;
17use mz_catalog::builtin::{
18 BuiltinTable, MZ_AGGREGATES, MZ_ARRAY_TYPES, MZ_BASE_TYPES, MZ_CLUSTER_REPLICA_SIZE_INTERNAL,
19 MZ_CLUSTER_REPLICA_SIZES, MZ_COLUMNS, MZ_EGRESS_IPS, MZ_FUNCTIONS,
20 MZ_HISTORY_RETENTION_STRATEGIES, MZ_INDEX_COLUMNS, MZ_LICENSE_KEYS, MZ_LIST_TYPES,
21 MZ_MAP_TYPES, MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES, MZ_OBJECT_GLOBAL_IDS, MZ_OPERATORS,
22 MZ_PSEUDO_TYPES, MZ_REPLACEMENTS, MZ_ROLE_AUTH, MZ_SESSIONS, MZ_SOURCE_REFERENCES,
23 MZ_STORAGE_USAGE_BY_SHARD, MZ_SUBSCRIPTIONS, MZ_TYPE_PG_METADATA, MZ_TYPES,
24 MZ_WEBHOOKS_SOURCES,
25};
26use mz_catalog::durable::SourceReferences;
27use mz_catalog::memory::error::Error;
28use mz_catalog::memory::objects::{
29 CatalogEntry, CatalogItem, DataSourceDesc, Func, Index, MaterializedView, Table,
30 TableDataSource, Type,
31};
32use mz_expr::MirScalarExpr;
33use mz_license_keys::ValidatedLicenseKey;
34use mz_orchestrator::{CpuLimit, DiskLimit, MemoryLimit};
35use mz_ore::cast::CastFrom;
36use mz_ore::collections::CollectionExt;
37use mz_persist_client::batch::ProtoBatch;
38use mz_repr::adt::array::ArrayDimension;
39use mz_repr::adt::interval::Interval;
40use mz_repr::adt::jsonb::Jsonb;
41use mz_repr::adt::mz_acl_item::PrivilegeMap;
42use mz_repr::refresh_schedule::RefreshEvery;
43use mz_repr::role_id::RoleId;
44use mz_repr::{
45 CatalogItemId, Datum, Diff, GlobalId, ReprColumnType, Row, RowPacker, SqlScalarType, Timestamp,
46};
47use mz_sql::ast::{CreateIndexStatement, Statement};
48use mz_sql::catalog::{CatalogType, TypeCategory};
49use mz_sql::func::FuncImplCatalogDetails;
50use mz_sql::names::SchemaSpecifier;
51use mz_sql_parser::ast::display::AstDisplay;
52use mz_storage_client::client::TableData;
53use smallvec::smallvec;
54use uuid::Uuid;
55
56use crate::active_compute_sink::ActiveSubscribe;
58use crate::catalog::CatalogState;
59use crate::coord::ConnMeta;
60
61#[derive(Debug, Clone)]
63pub struct BuiltinTableUpdate<T = CatalogItemId> {
64 pub id: T,
66 pub data: TableData,
68}
69
70impl<T> BuiltinTableUpdate<T> {
71 pub fn row(id: T, row: Row, diff: Diff) -> BuiltinTableUpdate<T> {
73 BuiltinTableUpdate {
74 id,
75 data: TableData::Rows(vec![(row, diff)]),
76 }
77 }
78
79 pub fn batch(id: T, batch: ProtoBatch) -> BuiltinTableUpdate<T> {
80 BuiltinTableUpdate {
81 id,
82 data: TableData::Batches(smallvec![batch]),
83 }
84 }
85}
86
87impl CatalogState {
88 pub fn resolve_builtin_table_updates(
89 &self,
90 builtin_table_update: Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
91 ) -> Vec<BuiltinTableUpdate<CatalogItemId>> {
92 builtin_table_update
93 .into_iter()
94 .map(|builtin_table_update| self.resolve_builtin_table_update(builtin_table_update))
95 .collect()
96 }
97
98 pub fn resolve_builtin_table_update(
99 &self,
100 BuiltinTableUpdate { id, data }: BuiltinTableUpdate<&'static BuiltinTable>,
101 ) -> BuiltinTableUpdate<CatalogItemId> {
102 let id = self.resolve_builtin_table(id);
103 BuiltinTableUpdate { id, data }
104 }
105
106 pub(super) fn pack_role_auth_update(
107 &self,
108 id: RoleId,
109 diff: Diff,
110 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
111 let role_auth = self.get_role_auth(&id);
112 let role = self.get_role(&id);
113 BuiltinTableUpdate::row(
114 &*MZ_ROLE_AUTH,
115 Row::pack_slice(&[
116 Datum::String(&role_auth.role_id.to_string()),
117 Datum::UInt32(role.oid),
118 match &role_auth.password_hash {
119 Some(hash) => Datum::String(hash),
120 None => Datum::Null,
121 },
122 Datum::TimestampTz(
123 mz_ore::now::to_datetime(role_auth.updated_at)
124 .try_into()
125 .expect("must fit"),
126 ),
127 ]),
128 diff,
129 )
130 }
131
132 pub(super) fn pack_item_update(
133 &self,
134 id: CatalogItemId,
135 diff: Diff,
136 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
137 let entry = self.get_entry(&id);
138 let oid = entry.oid();
139 let conn_id = entry.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
140 let schema_id = &self
141 .get_schema(
142 &entry.name().qualifiers.database_spec,
143 &entry.name().qualifiers.schema_spec,
144 conn_id,
145 )
146 .id;
147 let name = &entry.name().item;
148 let owner_id = entry.owner_id();
149 let privileges_row = self.pack_privilege_array_row(entry.privileges());
150 let privileges = privileges_row.unpack_first();
151 let mut updates = match entry.item() {
152 CatalogItem::Index(index) => self.pack_index_update(id, index, diff),
153 CatalogItem::Source(source) => {
154 match &source.data_source {
155 DataSourceDesc::Webhook { .. } => {
156 vec![self.pack_webhook_source_update(id, diff)]
157 }
158 DataSourceDesc::Ingestion { .. }
162 | DataSourceDesc::OldSyntaxIngestion { .. }
163 | DataSourceDesc::IngestionExport { .. }
164 | DataSourceDesc::Introspection(_)
165 | DataSourceDesc::Progress
166 | DataSourceDesc::Catalog => vec![],
167 }
168 }
169 CatalogItem::MaterializedView(mview) => {
170 self.pack_materialized_view_update(id, mview, diff)
171 }
172 CatalogItem::Sink(_) => vec![],
175 CatalogItem::Type(ty) => {
176 self.pack_type_update(id, oid, schema_id, name, owner_id, privileges, ty, diff)
177 }
178 CatalogItem::Func(func) => {
179 self.pack_func_update(id, schema_id, name, owner_id, func, diff)
180 }
181 CatalogItem::Table(_)
185 | CatalogItem::View(_)
186 | CatalogItem::Log(_)
187 | CatalogItem::Secret(_)
188 | CatalogItem::MetricSink(_) => vec![],
189 CatalogItem::Connection(_) => vec![],
194 };
195
196 if let Some(desc) = entry.relation_desc_latest() {
198 let defaults = match entry.item() {
199 CatalogItem::Table(Table {
200 data_source: TableDataSource::TableWrites { defaults },
201 ..
202 }) => Some(defaults),
203 _ => None,
204 };
205 for (i, (column_name, column_type)) in desc.iter().enumerate() {
206 let default: Option<String> = defaults.map(|d| d[i].to_ast_string_stable());
207 let default: Datum = default
208 .as_ref()
209 .map(|d| Datum::String(d))
210 .unwrap_or(Datum::Null);
211 let pgtype = mz_pgrepr::Type::from(&column_type.scalar_type);
212 let (type_name, type_oid) = match &column_type.scalar_type {
213 SqlScalarType::List {
214 custom_id: Some(custom_id),
215 ..
216 }
217 | SqlScalarType::Map {
218 custom_id: Some(custom_id),
219 ..
220 }
221 | SqlScalarType::Record {
222 custom_id: Some(custom_id),
223 ..
224 } => {
225 let entry = self.get_entry(custom_id);
226 let name = &*entry.name().item;
241 let oid = entry.oid();
242 (name, oid)
243 }
244 _ => (pgtype.name(), pgtype.oid()),
245 };
246 updates.push(BuiltinTableUpdate::row(
247 &*MZ_COLUMNS,
248 Row::pack_slice(&[
249 Datum::String(&id.to_string()),
250 Datum::String(column_name),
251 Datum::UInt64(u64::cast_from(i + 1)),
252 Datum::from(column_type.nullable),
253 Datum::String(type_name),
254 default,
255 Datum::UInt32(type_oid),
256 Datum::Int32(pgtype.typmod()),
257 ]),
258 diff,
259 ));
260 }
261 }
262
263 if let Some(cw) = entry.item().initial_logical_compaction_window() {
265 updates.push(self.pack_history_retention_strategy_update(id, cw, diff));
266 }
267
268 updates.extend(Self::pack_item_global_id_update(entry, diff));
269
270 updates
271 }
272
273 fn pack_item_global_id_update(
274 entry: &CatalogEntry,
275 diff: Diff,
276 ) -> impl Iterator<Item = BuiltinTableUpdate<&'static BuiltinTable>> + use<'_> {
277 let id = entry.id().to_string();
278 let global_ids = entry.global_ids();
279 global_ids.map(move |global_id| {
280 BuiltinTableUpdate::row(
281 &*MZ_OBJECT_GLOBAL_IDS,
282 Row::pack_slice(&[Datum::String(&id), Datum::String(&global_id.to_string())]),
283 diff,
284 )
285 })
286 }
287
288 fn pack_history_retention_strategy_update(
289 &self,
290 id: CatalogItemId,
291 cw: CompactionWindow,
292 diff: Diff,
293 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
294 let cw: u64 = cw.comparable_timestamp().into();
295 let cw = Jsonb::from_serde_json(serde_json::Value::Number(serde_json::Number::from(cw)))
296 .expect("must serialize");
297 BuiltinTableUpdate::row(
298 &*MZ_HISTORY_RETENTION_STRATEGIES,
299 Row::pack_slice(&[
300 Datum::String(&id.to_string()),
301 Datum::String("FOR"),
303 cw.into_row().into_element(),
304 ]),
305 diff,
306 )
307 }
308
309 fn pack_materialized_view_update(
310 &self,
311 id: CatalogItemId,
312 mview: &MaterializedView,
313 diff: Diff,
314 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
315 let mut updates = Vec::new();
316
317 if let Some(refresh_schedule) = &mview.refresh_schedule {
318 assert!(!refresh_schedule.is_empty());
321 for RefreshEvery {
322 interval,
323 aligned_to,
324 } in refresh_schedule.everies.iter()
325 {
326 let aligned_to_dt = mz_ore::now::to_datetime(
327 <&Timestamp as TryInto<u64>>::try_into(aligned_to).expect("undoes planning"),
328 );
329 updates.push(BuiltinTableUpdate::row(
330 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
331 Row::pack_slice(&[
332 Datum::String(&id.to_string()),
333 Datum::String("every"),
334 Datum::Interval(
335 Interval::from_duration(interval).expect(
336 "planning ensured that this is convertible back to Interval",
337 ),
338 ),
339 Datum::TimestampTz(aligned_to_dt.try_into().expect("undoes planning")),
340 Datum::Null,
341 ]),
342 diff,
343 ));
344 }
345 for at in refresh_schedule.ats.iter() {
346 let at_dt = mz_ore::now::to_datetime(
347 <&Timestamp as TryInto<u64>>::try_into(at).expect("undoes planning"),
348 );
349 updates.push(BuiltinTableUpdate::row(
350 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
351 Row::pack_slice(&[
352 Datum::String(&id.to_string()),
353 Datum::String("at"),
354 Datum::Null,
355 Datum::Null,
356 Datum::TimestampTz(at_dt.try_into().expect("undoes planning")),
357 ]),
358 diff,
359 ));
360 }
361 } else {
362 updates.push(BuiltinTableUpdate::row(
363 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
364 Row::pack_slice(&[
365 Datum::String(&id.to_string()),
366 Datum::String("on-commit"),
367 Datum::Null,
368 Datum::Null,
369 Datum::Null,
370 ]),
371 diff,
372 ));
373 }
374
375 if let Some(target_id) = mview.replacement_target {
376 updates.push(BuiltinTableUpdate::row(
377 &*MZ_REPLACEMENTS,
378 Row::pack_slice(&[
379 Datum::String(&id.to_string()),
380 Datum::String(&target_id.to_string()),
381 ]),
382 diff,
383 ));
384 }
385
386 updates
387 }
388
389 fn pack_index_update(
390 &self,
391 id: CatalogItemId,
392 index: &Index,
393 diff: Diff,
394 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
395 let mut updates = vec![];
396
397 let create_stmt = mz_sql::parse::parse(&index.create_sql)
398 .unwrap_or_else(|e| {
399 panic!(
400 "create_sql cannot be invalid: `{}` --- error: `{}`",
401 index.create_sql, e
402 )
403 })
404 .into_element()
405 .ast;
406
407 let key_sqls = match &create_stmt {
408 Statement::CreateIndex(CreateIndexStatement { key_parts, .. }) => key_parts
409 .as_ref()
410 .expect("key_parts is filled in during planning"),
411 _ => unreachable!(),
412 };
413
414 let on_entry = self.get_entry_by_global_id(&index.on);
415 let on_desc = on_entry
416 .relation_desc()
417 .expect("can only create indexes on items with a valid description");
418 let repr_col_types: Vec<ReprColumnType> = on_desc
419 .typ()
420 .column_types
421 .iter()
422 .map(ReprColumnType::from)
423 .collect();
424 for (i, key) in index.keys.iter().enumerate() {
425 let nullable = key.typ(&repr_col_types).nullable;
426 let seq_in_index = u64::cast_from(i + 1);
427 let key_sql = key_sqls
428 .get(i)
429 .expect("missing sql information for index key")
430 .to_ast_string_simple();
431 let (field_number, expression) = match key {
432 MirScalarExpr::Column(col, _) => {
433 (Datum::UInt64(u64::cast_from(*col + 1)), Datum::Null)
434 }
435 _ => (Datum::Null, Datum::String(&key_sql)),
436 };
437 updates.push(BuiltinTableUpdate::row(
438 &*MZ_INDEX_COLUMNS,
439 Row::pack_slice(&[
440 Datum::String(&id.to_string()),
441 Datum::UInt64(seq_in_index),
442 field_number,
443 expression,
444 Datum::from(nullable),
445 ]),
446 diff,
447 ));
448 }
449
450 updates
451 }
452
453 fn pack_type_update(
454 &self,
455 id: CatalogItemId,
456 oid: u32,
457 schema_id: &SchemaSpecifier,
458 name: &str,
459 owner_id: &RoleId,
460 privileges: Datum,
461 typ: &Type,
462 diff: Diff,
463 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
464 let mut out = vec![];
465
466 let redacted = typ.create_sql.as_ref().map(|create_sql| {
467 mz_sql::parse::parse(create_sql)
468 .unwrap_or_else(|_| panic!("create_sql cannot be invalid: {}", create_sql))
469 .into_element()
470 .ast
471 .to_ast_string_redacted()
472 });
473
474 out.push(BuiltinTableUpdate::row(
475 &*MZ_TYPES,
476 Row::pack_slice(&[
477 Datum::String(&id.to_string()),
478 Datum::UInt32(oid),
479 Datum::String(&schema_id.to_string()),
480 Datum::String(name),
481 Datum::String(&TypeCategory::from_catalog_type(&typ.details.typ).to_string()),
482 Datum::String(&owner_id.to_string()),
483 privileges,
484 if let Some(create_sql) = &typ.create_sql {
485 Datum::String(create_sql)
486 } else {
487 Datum::Null
488 },
489 if let Some(redacted) = &redacted {
490 Datum::String(redacted)
491 } else {
492 Datum::Null
493 },
494 ]),
495 diff,
496 ));
497
498 let mut row = Row::default();
499 let mut packer = row.packer();
500
501 fn append_modifier(packer: &mut RowPacker<'_>, mods: &[i64]) {
502 if mods.is_empty() {
503 packer.push(Datum::Null);
504 } else {
505 packer.push_list(mods.iter().map(|m| Datum::Int64(*m)));
506 }
507 }
508
509 let index_id = match &typ.details.typ {
510 CatalogType::Array {
511 element_reference: element_id,
512 } => {
513 packer.push(Datum::String(&id.to_string()));
514 packer.push(Datum::String(&element_id.to_string()));
515 &MZ_ARRAY_TYPES
516 }
517 CatalogType::List {
518 element_reference: element_id,
519 element_modifiers,
520 } => {
521 packer.push(Datum::String(&id.to_string()));
522 packer.push(Datum::String(&element_id.to_string()));
523 append_modifier(&mut packer, element_modifiers);
524 &MZ_LIST_TYPES
525 }
526 CatalogType::Map {
527 key_reference: key_id,
528 value_reference: value_id,
529 key_modifiers,
530 value_modifiers,
531 } => {
532 packer.push(Datum::String(&id.to_string()));
533 packer.push(Datum::String(&key_id.to_string()));
534 packer.push(Datum::String(&value_id.to_string()));
535 append_modifier(&mut packer, key_modifiers);
536 append_modifier(&mut packer, value_modifiers);
537 &MZ_MAP_TYPES
538 }
539 CatalogType::Pseudo => {
540 packer.push(Datum::String(&id.to_string()));
541 &MZ_PSEUDO_TYPES
542 }
543 _ => {
544 packer.push(Datum::String(&id.to_string()));
545 &MZ_BASE_TYPES
546 }
547 };
548 out.push(BuiltinTableUpdate::row(index_id, row, diff));
549
550 if let Some(pg_metadata) = &typ.details.pg_metadata {
551 out.push(BuiltinTableUpdate::row(
552 &*MZ_TYPE_PG_METADATA,
553 Row::pack_slice(&[
554 Datum::String(&id.to_string()),
555 Datum::UInt32(pg_metadata.typinput_oid),
556 Datum::UInt32(pg_metadata.typreceive_oid),
557 Datum::UInt32(pg_metadata.typsend_oid),
558 ]),
559 diff,
560 ));
561 }
562
563 out
564 }
565
566 fn pack_func_update(
567 &self,
568 id: CatalogItemId,
569 schema_id: &SchemaSpecifier,
570 name: &str,
571 owner_id: &RoleId,
572 func: &Func,
573 diff: Diff,
574 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
575 let mut updates = vec![];
576 for func_impl_details in func.inner.func_impls() {
577 let arg_type_ids = func_impl_details
578 .arg_typs
579 .iter()
580 .map(|typ| self.get_system_type(typ).id().to_string())
581 .collect::<Vec<_>>();
582
583 let mut row = Row::default();
584 row.packer()
585 .try_push_array(
586 &[ArrayDimension {
587 lower_bound: 1,
588 length: arg_type_ids.len(),
589 }],
590 arg_type_ids.iter().map(|id| Datum::String(id)),
591 )
592 .expect(
593 "arg_type_ids is 1 dimensional, and its length is used for the array length",
594 );
595 let arg_type_ids = row.unpack_first();
596
597 updates.push(BuiltinTableUpdate::row(
598 &*MZ_FUNCTIONS,
599 Row::pack_slice(&[
600 Datum::String(&id.to_string()),
601 Datum::UInt32(func_impl_details.oid),
602 Datum::String(&schema_id.to_string()),
603 Datum::String(name),
604 arg_type_ids,
605 Datum::from(
606 func_impl_details
607 .variadic_typ
608 .map(|typ| self.get_system_type(typ).id().to_string())
609 .as_deref(),
610 ),
611 Datum::from(
612 func_impl_details
613 .return_typ
614 .map(|typ| self.get_system_type(typ).id().to_string())
615 .as_deref(),
616 ),
617 func_impl_details.return_is_set.into(),
618 Datum::String(&owner_id.to_string()),
619 ]),
620 diff,
621 ));
622
623 if let mz_sql::func::Func::Aggregate(_) = func.inner {
624 updates.push(BuiltinTableUpdate::row(
625 &*MZ_AGGREGATES,
626 Row::pack_slice(&[
627 Datum::UInt32(func_impl_details.oid),
628 Datum::String("n"),
630 Datum::Int16(0),
631 ]),
632 diff,
633 ));
634 }
635 }
636 updates
637 }
638
639 pub fn pack_op_update(
640 &self,
641 operator: &str,
642 func_impl_details: FuncImplCatalogDetails,
643 diff: Diff,
644 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
645 let arg_type_ids = func_impl_details
646 .arg_typs
647 .iter()
648 .map(|typ| self.get_system_type(typ).id().to_string())
649 .collect::<Vec<_>>();
650
651 let mut row = Row::default();
652 row.packer()
653 .try_push_array(
654 &[ArrayDimension {
655 lower_bound: 1,
656 length: arg_type_ids.len(),
657 }],
658 arg_type_ids.iter().map(|id| Datum::String(id)),
659 )
660 .expect("arg_type_ids is 1 dimensional, and its length is used for the array length");
661 let arg_type_ids = row.unpack_first();
662
663 BuiltinTableUpdate::row(
664 &*MZ_OPERATORS,
665 Row::pack_slice(&[
666 Datum::UInt32(func_impl_details.oid),
667 Datum::String(operator),
668 arg_type_ids,
669 Datum::from(
670 func_impl_details
671 .return_typ
672 .map(|typ| self.get_system_type(typ).id().to_string())
673 .as_deref(),
674 ),
675 ]),
676 diff,
677 )
678 }
679
680 pub fn pack_storage_usage_update(
681 &self,
682 VersionedStorageUsage::V1(event): VersionedStorageUsage,
683 diff: Diff,
684 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
685 let id = &MZ_STORAGE_USAGE_BY_SHARD;
686 let row = Row::pack_slice(&[
687 Datum::UInt64(event.id),
688 Datum::from(event.shard_id.as_deref()),
689 Datum::UInt64(event.size_bytes),
690 Datum::TimestampTz(
691 mz_ore::now::to_datetime(event.collection_timestamp)
692 .try_into()
693 .expect("must fit"),
694 ),
695 ]);
696 BuiltinTableUpdate::row(id, row, diff)
697 }
698
699 pub fn pack_egress_ip_update(
700 &self,
701 ip: &IpNet,
702 ) -> Result<BuiltinTableUpdate<&'static BuiltinTable>, Error> {
703 let id = &MZ_EGRESS_IPS;
704 let addr = ip.network();
705 let row = Row::pack_slice(&[
706 Datum::String(&addr.to_string()),
707 Datum::Int32(ip.prefix_len().into()),
708 Datum::String(&format!("{}/{}", addr, ip.prefix_len())),
709 ]);
710 Ok(BuiltinTableUpdate::row(id, row, Diff::ONE))
711 }
712
713 pub fn pack_license_key_update(
714 &self,
715 license_key: &ValidatedLicenseKey,
716 ) -> Result<BuiltinTableUpdate<&'static BuiltinTable>, Error> {
717 let id = &MZ_LICENSE_KEYS;
718 let row = Row::pack_slice(&[
719 Datum::String(&license_key.id),
720 Datum::String(&license_key.organization),
721 Datum::String(&license_key.environment_id),
722 Datum::TimestampTz(
723 mz_ore::now::to_datetime(license_key.expiration * 1000)
724 .try_into()
725 .expect("must fit"),
726 ),
727 Datum::TimestampTz(
728 mz_ore::now::to_datetime(license_key.not_before * 1000)
729 .try_into()
730 .expect("must fit"),
731 ),
732 ]);
733 Ok(BuiltinTableUpdate::row(id, row, Diff::ONE))
734 }
735
736 pub fn pack_all_replica_size_updates(&self) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
737 let mut updates = Vec::new();
738 for (size, alloc) in &self.cluster_replica_sizes.0 {
739 let DiskLimit(ByteSize(disk_bytes)) =
742 (alloc.disk_limit).unwrap_or(DiskLimit::ARBITRARY);
743
744 let internal_row = Row::pack_slice(&[
753 size.as_str().into(),
754 Datum::from(alloc.swap_enabled),
755 disk_bytes.into(),
756 ]);
757 updates.push(BuiltinTableUpdate::row(
758 &*MZ_CLUSTER_REPLICA_SIZE_INTERNAL,
759 internal_row,
760 Diff::ONE,
761 ));
762
763 if alloc.disabled {
764 continue;
765 }
766
767 let cpu_limit = alloc.cpu_limit.unwrap_or(CpuLimit::MAX);
768 let MemoryLimit(ByteSize(memory_bytes)) =
769 (alloc.memory_limit).unwrap_or(MemoryLimit::MAX);
770
771 let row = Row::pack_slice(&[
772 size.as_str().into(),
773 u64::cast_from(alloc.scale).into(),
774 u64::cast_from(alloc.workers).into(),
775 cpu_limit.as_nanocpus().into(),
776 memory_bytes.into(),
777 disk_bytes.into(),
778 (alloc.credits_per_hour).into(),
779 ]);
780
781 updates.push(BuiltinTableUpdate::row(
782 &*MZ_CLUSTER_REPLICA_SIZES,
783 row,
784 Diff::ONE,
785 ));
786 }
787
788 updates
789 }
790
791 pub fn pack_subscribe_update(
792 &self,
793 id: GlobalId,
794 subscribe: &ActiveSubscribe,
795 session_uuid: Uuid,
796 diff: Diff,
797 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
798 let mut row = Row::default();
799 let mut packer = row.packer();
800 packer.push(Datum::String(&id.to_string()));
801 packer.push(Datum::Uuid(session_uuid));
802 packer.push(Datum::String(&subscribe.cluster_id.to_string()));
803
804 let start_dt = mz_ore::now::to_datetime(subscribe.start_time);
805 packer.push(Datum::TimestampTz(start_dt.try_into().expect("must fit")));
806
807 let depends_on: Vec<_> = subscribe
808 .depends_on
809 .iter()
810 .map(|id| id.to_string())
811 .collect();
812 packer.push_list(depends_on.iter().map(|s| Datum::String(s)));
813
814 BuiltinTableUpdate::row(&*MZ_SUBSCRIPTIONS, row, diff)
815 }
816
817 pub fn pack_session_update(
818 &self,
819 conn: &ConnMeta,
820 diff: Diff,
821 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
822 let connect_dt = mz_ore::now::to_datetime(conn.connected_at());
823 BuiltinTableUpdate::row(
824 &*MZ_SESSIONS,
825 Row::pack_slice(&[
826 Datum::Uuid(conn.uuid()),
827 Datum::UInt32(conn.conn_id().unhandled()),
828 Datum::String(&conn.authenticated_role_id().to_string()),
829 Datum::from(conn.client_ip().map(|ip| ip.to_string()).as_deref()),
830 Datum::TimestampTz(connect_dt.try_into().expect("must fit")),
831 ]),
832 diff,
833 )
834 }
835
836 fn pack_privilege_array_row(&self, privileges: &PrivilegeMap) -> Row {
837 let mut row = Row::default();
838 let flat_privileges: Vec<_> = privileges.all_values_owned().collect();
839 row.packer()
840 .try_push_array(
841 &[ArrayDimension {
842 lower_bound: 1,
843 length: flat_privileges.len(),
844 }],
845 flat_privileges
846 .into_iter()
847 .map(|mz_acl_item| Datum::MzAclItem(mz_acl_item.clone())),
848 )
849 .expect("privileges is 1 dimensional, and its length is used for the array length");
850 row
851 }
852
853 pub fn pack_webhook_source_update(
854 &self,
855 item_id: CatalogItemId,
856 diff: Diff,
857 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
858 let url = self
859 .try_get_webhook_url(&item_id)
860 .expect("webhook source should exist");
861 let url = url.to_string();
862 let name = &self.get_entry(&item_id).name().item;
863 let id_str = item_id.to_string();
864
865 BuiltinTableUpdate::row(
866 &*MZ_WEBHOOKS_SOURCES,
867 Row::pack_slice(&[
868 Datum::String(&id_str),
869 Datum::String(name),
870 Datum::String(&url),
871 ]),
872 diff,
873 )
874 }
875
876 pub fn pack_source_references_update(
877 &self,
878 source_references: &SourceReferences,
879 diff: Diff,
880 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
881 let source_id = source_references.source_id.to_string();
882 let updated_at = &source_references.updated_at;
883 source_references
884 .references
885 .iter()
886 .map(|reference| {
887 let mut row = Row::default();
888 let mut packer = row.packer();
889 packer.extend([
890 Datum::String(&source_id),
891 reference
892 .namespace
893 .as_ref()
894 .map(|s| Datum::String(s))
895 .unwrap_or(Datum::Null),
896 Datum::String(&reference.name),
897 Datum::TimestampTz(
898 mz_ore::now::to_datetime(*updated_at)
899 .try_into()
900 .expect("must fit"),
901 ),
902 ]);
903 if reference.columns.len() > 0 {
904 packer
905 .try_push_array(
906 &[ArrayDimension {
907 lower_bound: 1,
908 length: reference.columns.len(),
909 }],
910 reference.columns.iter().map(|col| Datum::String(col)),
911 )
912 .expect(
913 "columns is 1 dimensional, and its length is used for the array length",
914 );
915 } else {
916 packer.push(Datum::Null);
917 }
918
919 BuiltinTableUpdate::row(&*MZ_SOURCE_REFERENCES, row, diff)
920 })
921 .collect()
922 }
923}