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