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_AWS_CONNECTIONS,
19 MZ_AWS_PRIVATELINK_CONNECTIONS, MZ_BASE_TYPES, MZ_CLUSTER_REPLICA_SIZE_INTERNAL,
20 MZ_CLUSTER_REPLICA_SIZES, MZ_COLUMNS, MZ_EGRESS_IPS, MZ_FUNCTIONS,
21 MZ_HISTORY_RETENTION_STRATEGIES, MZ_ICEBERG_SINKS, MZ_INDEX_COLUMNS, MZ_KAFKA_CONNECTIONS,
22 MZ_KAFKA_SINKS, MZ_KAFKA_SOURCE_TABLES, MZ_LICENSE_KEYS, MZ_LIST_TYPES, MZ_MAP_TYPES,
23 MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES, MZ_MYSQL_SOURCE_TABLES, MZ_OBJECT_DEPENDENCIES,
24 MZ_OBJECT_GLOBAL_IDS, MZ_OPERATORS, MZ_POSTGRES_SOURCE_TABLES, MZ_PSEUDO_TYPES,
25 MZ_REPLACEMENTS, MZ_ROLE_AUTH, MZ_SESSIONS, MZ_SINKS, MZ_SOURCE_REFERENCES,
26 MZ_SQL_SERVER_SOURCE_TABLES, MZ_SSH_TUNNEL_CONNECTIONS, MZ_STORAGE_USAGE_BY_SHARD,
27 MZ_SUBSCRIPTIONS, MZ_TABLES, MZ_TYPE_PG_METADATA, MZ_TYPES, MZ_VIEWS, MZ_WEBHOOKS_SOURCES,
28};
29use mz_catalog::config::AwsPrincipalContext;
30use mz_catalog::durable::SourceReferences;
31use mz_catalog::memory::error::Error;
32use mz_catalog::memory::objects::{
33 CatalogEntry, CatalogItem, Connection, DataSourceDesc, Func, Index, MaterializedView, Sink,
34 Table, TableDataSource, Type, View,
35};
36use mz_expr::MirScalarExpr;
37use mz_license_keys::ValidatedLicenseKey;
38use mz_orchestrator::{CpuLimit, DiskLimit, MemoryLimit};
39use mz_ore::cast::CastFrom;
40use mz_ore::collections::CollectionExt;
41use mz_persist_client::batch::ProtoBatch;
42use mz_repr::adt::array::ArrayDimension;
43use mz_repr::adt::interval::Interval;
44use mz_repr::adt::jsonb::Jsonb;
45use mz_repr::adt::mz_acl_item::PrivilegeMap;
46use mz_repr::refresh_schedule::RefreshEvery;
47use mz_repr::role_id::RoleId;
48use mz_repr::{
49 CatalogItemId, Datum, Diff, GlobalId, ReprColumnType, Row, RowPacker, SqlScalarType, Timestamp,
50};
51use mz_sql::ast::{CreateIndexStatement, Statement, UnresolvedItemName};
52use mz_sql::catalog::{CatalogType, TypeCategory};
53use mz_sql::func::FuncImplCatalogDetails;
54use mz_sql::names::SchemaSpecifier;
55use mz_sql::plan::{ConnectionDetails, SshKey};
56use mz_sql_parser::ast::display::AstDisplay;
57use mz_storage_client::client::TableData;
58use mz_storage_types::connections::KafkaConnection;
59use mz_storage_types::connections::aws::{AwsAuth, AwsConnection};
60use mz_storage_types::connections::inline::ReferencedConnection;
61use mz_storage_types::connections::string_or_secret::StringOrSecret;
62use mz_storage_types::sinks::{IcebergSinkConnection, KafkaSinkConnection, StorageSinkConnection};
63use mz_storage_types::sources::SourceConnection;
64use smallvec::smallvec;
65
66use crate::active_compute_sink::ActiveSubscribe;
68use crate::catalog::CatalogState;
69use crate::coord::ConnMeta;
70
71#[derive(Debug, Clone)]
73pub struct BuiltinTableUpdate<T = CatalogItemId> {
74 pub id: T,
76 pub data: TableData,
78}
79
80impl<T> BuiltinTableUpdate<T> {
81 pub fn row(id: T, row: Row, diff: Diff) -> BuiltinTableUpdate<T> {
83 BuiltinTableUpdate {
84 id,
85 data: TableData::Rows(vec![(row, diff)]),
86 }
87 }
88
89 pub fn batch(id: T, batch: ProtoBatch) -> BuiltinTableUpdate<T> {
90 BuiltinTableUpdate {
91 id,
92 data: TableData::Batches(smallvec![batch]),
93 }
94 }
95}
96
97impl CatalogState {
98 pub fn resolve_builtin_table_updates(
99 &self,
100 builtin_table_update: Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
101 ) -> Vec<BuiltinTableUpdate<CatalogItemId>> {
102 builtin_table_update
103 .into_iter()
104 .map(|builtin_table_update| self.resolve_builtin_table_update(builtin_table_update))
105 .collect()
106 }
107
108 pub fn resolve_builtin_table_update(
109 &self,
110 BuiltinTableUpdate { id, data }: BuiltinTableUpdate<&'static BuiltinTable>,
111 ) -> BuiltinTableUpdate<CatalogItemId> {
112 let id = self.resolve_builtin_table(id);
113 BuiltinTableUpdate { id, data }
114 }
115
116 pub fn pack_depends_update(
117 &self,
118 depender: CatalogItemId,
119 dependee: CatalogItemId,
120 diff: Diff,
121 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
122 let row = Row::pack_slice(&[
123 Datum::String(&depender.to_string()),
124 Datum::String(&dependee.to_string()),
125 ]);
126 BuiltinTableUpdate::row(&*MZ_OBJECT_DEPENDENCIES, row, diff)
127 }
128
129 pub(super) fn pack_role_auth_update(
130 &self,
131 id: RoleId,
132 diff: Diff,
133 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
134 let role_auth = self.get_role_auth(&id);
135 let role = self.get_role(&id);
136 BuiltinTableUpdate::row(
137 &*MZ_ROLE_AUTH,
138 Row::pack_slice(&[
139 Datum::String(&role_auth.role_id.to_string()),
140 Datum::UInt32(role.oid),
141 match &role_auth.password_hash {
142 Some(hash) => Datum::String(hash),
143 None => Datum::Null,
144 },
145 Datum::TimestampTz(
146 mz_ore::now::to_datetime(role_auth.updated_at)
147 .try_into()
148 .expect("must fit"),
149 ),
150 ]),
151 diff,
152 )
153 }
154
155 pub(super) fn pack_item_update(
156 &self,
157 id: CatalogItemId,
158 diff: Diff,
159 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
160 let entry = self.get_entry(&id);
161 let oid = entry.oid();
162 let conn_id = entry.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
163 let schema_id = &self
164 .get_schema(
165 &entry.name().qualifiers.database_spec,
166 &entry.name().qualifiers.schema_spec,
167 conn_id,
168 )
169 .id;
170 let name = &entry.name().item;
171 let owner_id = entry.owner_id();
172 let privileges_row = self.pack_privilege_array_row(entry.privileges());
173 let privileges = privileges_row.unpack_first();
174 let mut updates = match entry.item() {
175 CatalogItem::Index(index) => self.pack_index_update(id, index, diff),
176 CatalogItem::Table(table) => {
177 let mut updates = self
178 .pack_table_update(id, oid, schema_id, name, owner_id, privileges, diff, table);
179
180 if let TableDataSource::DataSource {
181 desc: data_source,
182 timeline: _,
183 } = &table.data_source
184 {
185 updates.extend(match data_source {
186 DataSourceDesc::IngestionExport {
187 ingestion_id,
188 external_reference: UnresolvedItemName(external_reference),
189 details: _,
190 data_config: _,
191 } => {
192 let ingestion_entry = self
193 .get_entry(ingestion_id)
194 .source_desc()
195 .expect("primary source exists")
196 .expect("primary source is a source");
197
198 match ingestion_entry.connection.name() {
199 "postgres" => {
200 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 3);
201 let schema_name = external_reference[1].as_str();
207 let table_name = external_reference[2].as_str();
208
209 self.pack_postgres_source_tables_update(
210 id,
211 schema_name,
212 table_name,
213 diff,
214 )
215 }
216 "mysql" => {
217 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 2);
218 let schema_name = external_reference[0].as_str();
219 let table_name = external_reference[1].as_str();
220
221 self.pack_mysql_source_tables_update(
222 id,
223 schema_name,
224 table_name,
225 diff,
226 )
227 }
228 "sql-server" => {
229 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 3);
230 let schema_name = external_reference[1].as_str();
235 let table_name = external_reference[2].as_str();
236
237 self.pack_sql_server_source_table_update(
238 id,
239 schema_name,
240 table_name,
241 diff,
242 )
243 }
244 "load-generator" => vec![],
247 "kafka" => {
248 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 1);
249 let topic = external_reference[0].as_str();
250 let envelope = data_source.envelope();
251 let (key_format, value_format) = data_source.formats();
252
253 self.pack_kafka_source_tables_update(
254 id,
255 topic,
256 envelope,
257 key_format,
258 value_format,
259 diff,
260 )
261 }
262 s => unreachable!("{s} sources do not have tables"),
263 }
264 }
265 DataSourceDesc::Ingestion { .. }
266 | DataSourceDesc::OldSyntaxIngestion { .. }
267 | DataSourceDesc::Introspection(_)
268 | DataSourceDesc::Progress
269 | DataSourceDesc::Webhook { .. }
270 | DataSourceDesc::Catalog => vec![],
271 });
272 }
273
274 updates
275 }
276 CatalogItem::Source(source) => {
277 match &source.data_source {
278 DataSourceDesc::Ingestion { .. }
279 | DataSourceDesc::OldSyntaxIngestion { .. } => vec![],
280 DataSourceDesc::IngestionExport {
281 ingestion_id,
282 external_reference: UnresolvedItemName(external_reference),
283 details: _,
284 data_config: _,
285 } => {
286 let ingestion_entry = self
287 .get_entry(ingestion_id)
288 .source_desc()
289 .expect("primary source exists")
290 .expect("primary source is a source");
291
292 match ingestion_entry.connection.name() {
293 "postgres" => {
294 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 3);
295 let schema_name = external_reference[1].as_str();
301 let table_name = external_reference[2].as_str();
302
303 self.pack_postgres_source_tables_update(
304 id,
305 schema_name,
306 table_name,
307 diff,
308 )
309 }
310 "mysql" => {
311 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 2);
312 let schema_name = external_reference[0].as_str();
313 let table_name = external_reference[1].as_str();
314
315 self.pack_mysql_source_tables_update(
316 id,
317 schema_name,
318 table_name,
319 diff,
320 )
321 }
322 "sql-server" => {
323 mz_ore::soft_assert_eq_no_log!(external_reference.len(), 3);
324 let schema_name = external_reference[1].as_str();
329 let table_name = external_reference[2].as_str();
330
331 self.pack_sql_server_source_table_update(
332 id,
333 schema_name,
334 table_name,
335 diff,
336 )
337 }
338 "load-generator" => vec![],
341 s => unreachable!("{s} sources do not have subsources"),
342 }
343 }
344 DataSourceDesc::Webhook { .. } => {
345 vec![self.pack_webhook_source_update(id, diff)]
346 }
347 DataSourceDesc::Introspection(_)
348 | DataSourceDesc::Progress
349 | DataSourceDesc::Catalog => vec![],
350 }
351 }
352 CatalogItem::View(view) => {
353 self.pack_view_update(id, oid, schema_id, name, owner_id, privileges, view, diff)
354 }
355 CatalogItem::MaterializedView(mview) => {
356 self.pack_materialized_view_update(id, mview, diff)
357 }
358 CatalogItem::Sink(sink) => {
359 self.pack_sink_update(id, oid, schema_id, name, owner_id, sink, diff)
360 }
361 CatalogItem::Type(ty) => {
362 self.pack_type_update(id, oid, schema_id, name, owner_id, privileges, ty, diff)
363 }
364 CatalogItem::Func(func) => {
365 self.pack_func_update(id, schema_id, name, owner_id, func, diff)
366 }
367 CatalogItem::Log(_) | CatalogItem::Secret(_) => vec![],
368 CatalogItem::Connection(connection) => {
369 self.pack_connection_update(id, connection, diff)
370 }
371 };
372
373 if !entry.item().is_temporary() {
374 for dependee in entry.item().references().items() {
377 updates.push(self.pack_depends_update(id, *dependee, diff))
378 }
379 }
380
381 if let Some(desc) = entry.relation_desc_latest() {
383 let defaults = match entry.item() {
384 CatalogItem::Table(Table {
385 data_source: TableDataSource::TableWrites { defaults },
386 ..
387 }) => Some(defaults),
388 _ => None,
389 };
390 for (i, (column_name, column_type)) in desc.iter().enumerate() {
391 let default: Option<String> = defaults.map(|d| d[i].to_ast_string_stable());
392 let default: Datum = default
393 .as_ref()
394 .map(|d| Datum::String(d))
395 .unwrap_or(Datum::Null);
396 let pgtype = mz_pgrepr::Type::from(&column_type.scalar_type);
397 let (type_name, type_oid) = match &column_type.scalar_type {
398 SqlScalarType::List {
399 custom_id: Some(custom_id),
400 ..
401 }
402 | SqlScalarType::Map {
403 custom_id: Some(custom_id),
404 ..
405 }
406 | SqlScalarType::Record {
407 custom_id: Some(custom_id),
408 ..
409 } => {
410 let entry = self.get_entry(custom_id);
411 let name = &*entry.name().item;
426 let oid = entry.oid();
427 (name, oid)
428 }
429 _ => (pgtype.name(), pgtype.oid()),
430 };
431 updates.push(BuiltinTableUpdate::row(
432 &*MZ_COLUMNS,
433 Row::pack_slice(&[
434 Datum::String(&id.to_string()),
435 Datum::String(column_name),
436 Datum::UInt64(u64::cast_from(i + 1)),
437 Datum::from(column_type.nullable),
438 Datum::String(type_name),
439 default,
440 Datum::UInt32(type_oid),
441 Datum::Int32(pgtype.typmod()),
442 ]),
443 diff,
444 ));
445 }
446 }
447
448 if let Some(cw) = entry.item().initial_logical_compaction_window() {
450 updates.push(self.pack_history_retention_strategy_update(id, cw, diff));
451 }
452
453 updates.extend(Self::pack_item_global_id_update(entry, diff));
454
455 updates
456 }
457
458 fn pack_item_global_id_update(
459 entry: &CatalogEntry,
460 diff: Diff,
461 ) -> impl Iterator<Item = BuiltinTableUpdate<&'static BuiltinTable>> + use<'_> {
462 let id = entry.id().to_string();
463 let global_ids = entry.global_ids();
464 global_ids.map(move |global_id| {
465 BuiltinTableUpdate::row(
466 &*MZ_OBJECT_GLOBAL_IDS,
467 Row::pack_slice(&[Datum::String(&id), Datum::String(&global_id.to_string())]),
468 diff,
469 )
470 })
471 }
472
473 fn pack_history_retention_strategy_update(
474 &self,
475 id: CatalogItemId,
476 cw: CompactionWindow,
477 diff: Diff,
478 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
479 let cw: u64 = cw.comparable_timestamp().into();
480 let cw = Jsonb::from_serde_json(serde_json::Value::Number(serde_json::Number::from(cw)))
481 .expect("must serialize");
482 BuiltinTableUpdate::row(
483 &*MZ_HISTORY_RETENTION_STRATEGIES,
484 Row::pack_slice(&[
485 Datum::String(&id.to_string()),
486 Datum::String("FOR"),
488 cw.into_row().into_element(),
489 ]),
490 diff,
491 )
492 }
493
494 fn pack_table_update(
495 &self,
496 id: CatalogItemId,
497 oid: u32,
498 schema_id: &SchemaSpecifier,
499 name: &str,
500 owner_id: &RoleId,
501 privileges: Datum,
502 diff: Diff,
503 table: &Table,
504 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
505 let redacted = table.create_sql.as_ref().map(|create_sql| {
506 mz_sql::parse::parse(create_sql)
507 .unwrap_or_else(|_| panic!("create_sql cannot be invalid: {}", create_sql))
508 .into_element()
509 .ast
510 .to_ast_string_redacted()
511 });
512 let source_id = if let TableDataSource::DataSource {
513 desc: DataSourceDesc::IngestionExport { ingestion_id, .. },
514 ..
515 } = &table.data_source
516 {
517 Some(ingestion_id.to_string())
518 } else {
519 None
520 };
521
522 vec![BuiltinTableUpdate::row(
523 &*MZ_TABLES,
524 Row::pack_slice(&[
525 Datum::String(&id.to_string()),
526 Datum::UInt32(oid),
527 Datum::String(&schema_id.to_string()),
528 Datum::String(name),
529 Datum::String(&owner_id.to_string()),
530 privileges,
531 if let Some(create_sql) = &table.create_sql {
532 Datum::String(create_sql)
533 } else {
534 Datum::Null
535 },
536 if let Some(redacted) = &redacted {
537 Datum::String(redacted)
538 } else {
539 Datum::Null
540 },
541 if let Some(source_id) = source_id.as_ref() {
542 Datum::String(source_id)
543 } else {
544 Datum::Null
545 },
546 ]),
547 diff,
548 )]
549 }
550
551 fn pack_postgres_source_tables_update(
552 &self,
553 id: CatalogItemId,
554 schema_name: &str,
555 table_name: &str,
556 diff: Diff,
557 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
558 vec![BuiltinTableUpdate::row(
559 &*MZ_POSTGRES_SOURCE_TABLES,
560 Row::pack_slice(&[
561 Datum::String(&id.to_string()),
562 Datum::String(schema_name),
563 Datum::String(table_name),
564 ]),
565 diff,
566 )]
567 }
568
569 fn pack_mysql_source_tables_update(
570 &self,
571 id: CatalogItemId,
572 schema_name: &str,
573 table_name: &str,
574 diff: Diff,
575 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
576 vec![BuiltinTableUpdate::row(
577 &*MZ_MYSQL_SOURCE_TABLES,
578 Row::pack_slice(&[
579 Datum::String(&id.to_string()),
580 Datum::String(schema_name),
581 Datum::String(table_name),
582 ]),
583 diff,
584 )]
585 }
586
587 fn pack_sql_server_source_table_update(
588 &self,
589 id: CatalogItemId,
590 schema_name: &str,
591 table_name: &str,
592 diff: Diff,
593 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
594 vec![BuiltinTableUpdate::row(
595 &*MZ_SQL_SERVER_SOURCE_TABLES,
596 Row::pack_slice(&[
597 Datum::String(&id.to_string()),
598 Datum::String(schema_name),
599 Datum::String(table_name),
600 ]),
601 diff,
602 )]
603 }
604
605 fn pack_kafka_source_tables_update(
606 &self,
607 id: CatalogItemId,
608 topic: &str,
609 envelope: Option<&str>,
610 key_format: Option<&str>,
611 value_format: Option<&str>,
612 diff: Diff,
613 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
614 vec![BuiltinTableUpdate::row(
615 &*MZ_KAFKA_SOURCE_TABLES,
616 Row::pack_slice(&[
617 Datum::String(&id.to_string()),
618 Datum::String(topic),
619 Datum::from(envelope),
620 Datum::from(key_format),
621 Datum::from(value_format),
622 ]),
623 diff,
624 )]
625 }
626
627 fn pack_connection_update(
628 &self,
629 id: CatalogItemId,
630 connection: &Connection,
631 diff: Diff,
632 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
633 let mut updates = vec![];
634 match connection.details {
635 ConnectionDetails::Kafka(ref kafka) => {
636 updates.extend(self.pack_kafka_connection_update(id, kafka, diff));
637 }
638 ConnectionDetails::Aws(ref aws_config) => {
639 match self.pack_aws_connection_update(id, aws_config, diff) {
640 Ok(update) => {
641 updates.push(update);
642 }
643 Err(e) => {
644 tracing::error!(%id, %e, "failed writing row to mz_aws_connections table");
645 }
646 }
647 }
648 ConnectionDetails::AwsPrivatelink(_) => {
649 if let Some(aws_principal_context) = self.aws_principal_context.as_ref() {
650 updates.push(self.pack_aws_privatelink_connection_update(
651 id,
652 aws_principal_context,
653 diff,
654 ));
655 } else {
656 tracing::error!(%id, "missing AWS principal context; cannot write row to mz_aws_privatelink_connections table");
657 }
658 }
659 ConnectionDetails::Ssh {
660 ref key_1,
661 ref key_2,
662 ..
663 } => {
664 updates.push(self.pack_ssh_tunnel_connection_update(id, key_1, key_2, diff));
665 }
666 ConnectionDetails::Csr(_)
667 | ConnectionDetails::GlueSchemaRegistry(_)
668 | ConnectionDetails::Gcp(_)
669 | ConnectionDetails::Postgres(_)
670 | ConnectionDetails::MySql(_)
671 | ConnectionDetails::SqlServer(_)
672 | ConnectionDetails::IcebergCatalog(_) => (),
673 };
674 updates
675 }
676
677 pub(crate) fn pack_ssh_tunnel_connection_update(
678 &self,
679 id: CatalogItemId,
680 key_1: &SshKey,
681 key_2: &SshKey,
682 diff: Diff,
683 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
684 BuiltinTableUpdate::row(
685 &*MZ_SSH_TUNNEL_CONNECTIONS,
686 Row::pack_slice(&[
687 Datum::String(&id.to_string()),
688 Datum::String(key_1.public_key().as_str()),
689 Datum::String(key_2.public_key().as_str()),
690 ]),
691 diff,
692 )
693 }
694
695 fn pack_kafka_connection_update(
696 &self,
697 id: CatalogItemId,
698 kafka: &KafkaConnection<ReferencedConnection>,
699 diff: Diff,
700 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
701 let progress_topic = kafka.progress_topic(&self.config.connection_context, id);
702 let mut row = Row::default();
703 row.packer()
704 .try_push_array(
705 &[ArrayDimension {
706 lower_bound: 1,
707 length: kafka.brokers.len(),
708 }],
709 kafka
710 .brokers
711 .iter()
712 .map(|broker| Datum::String(&broker.address)),
713 )
714 .expect("kafka.brokers is 1 dimensional, and its length is used for the array length");
715 let brokers = row.unpack_first();
716 vec![BuiltinTableUpdate::row(
717 &*MZ_KAFKA_CONNECTIONS,
718 Row::pack_slice(&[
719 Datum::String(&id.to_string()),
720 brokers,
721 Datum::String(&progress_topic),
722 ]),
723 diff,
724 )]
725 }
726
727 pub fn pack_aws_privatelink_connection_update(
728 &self,
729 connection_id: CatalogItemId,
730 aws_principal_context: &AwsPrincipalContext,
731 diff: Diff,
732 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
733 let id = &MZ_AWS_PRIVATELINK_CONNECTIONS;
734 let row = Row::pack_slice(&[
735 Datum::String(&connection_id.to_string()),
736 Datum::String(&aws_principal_context.to_principal_string(connection_id)),
737 ]);
738 BuiltinTableUpdate::row(id, row, diff)
739 }
740
741 pub fn pack_aws_connection_update(
742 &self,
743 connection_id: CatalogItemId,
744 aws_config: &AwsConnection,
745 diff: Diff,
746 ) -> Result<BuiltinTableUpdate<&'static BuiltinTable>, anyhow::Error> {
747 let id = &MZ_AWS_CONNECTIONS;
748
749 let mut access_key_id = None;
750 let mut access_key_id_secret_id = None;
751 let mut secret_access_key_secret_id = None;
752 let mut session_token = None;
753 let mut session_token_secret_id = None;
754 let mut assume_role_arn = None;
755 let mut assume_role_session_name = None;
756 let mut principal = None;
757 let mut external_id = None;
758 let mut example_trust_policy = None;
759 match &aws_config.auth {
760 AwsAuth::Credentials(credentials) => {
761 match &credentials.access_key_id {
762 StringOrSecret::String(s) => access_key_id = Some(s.as_str()),
763 StringOrSecret::Secret(s) => access_key_id_secret_id = Some(s.to_string()),
764 }
765 secret_access_key_secret_id = Some(credentials.secret_access_key.to_string());
766 match credentials.session_token.as_ref() {
767 None => (),
768 Some(StringOrSecret::String(s)) => session_token = Some(s.as_str()),
769 Some(StringOrSecret::Secret(s)) => {
770 session_token_secret_id = Some(s.to_string())
771 }
772 }
773 }
774 AwsAuth::AssumeRole(assume_role) => {
775 assume_role_arn = Some(assume_role.arn.as_str());
776 assume_role_session_name = assume_role.session_name.as_deref();
777 principal = self
778 .config
779 .connection_context
780 .aws_connection_role_arn
781 .as_deref();
782 external_id =
783 Some(assume_role.external_id(&self.config.connection_context, connection_id)?);
784 example_trust_policy = {
785 let policy = assume_role
786 .example_trust_policy(&self.config.connection_context, connection_id)?;
787 let policy = Jsonb::from_serde_json(policy).expect("valid json");
788 Some(policy.into_row())
789 };
790 }
791 }
792
793 let row = Row::pack_slice(&[
794 Datum::String(&connection_id.to_string()),
795 Datum::from(aws_config.endpoint.as_deref()),
796 Datum::from(aws_config.region.as_deref()),
797 Datum::from(access_key_id),
798 Datum::from(access_key_id_secret_id.as_deref()),
799 Datum::from(secret_access_key_secret_id.as_deref()),
800 Datum::from(session_token),
801 Datum::from(session_token_secret_id.as_deref()),
802 Datum::from(assume_role_arn),
803 Datum::from(assume_role_session_name),
804 Datum::from(principal),
805 Datum::from(external_id.as_deref()),
806 Datum::from(example_trust_policy.as_ref().map(|p| p.into_element())),
807 ]);
808
809 Ok(BuiltinTableUpdate::row(id, row, diff))
810 }
811
812 fn pack_view_update(
813 &self,
814 id: CatalogItemId,
815 oid: u32,
816 schema_id: &SchemaSpecifier,
817 name: &str,
818 owner_id: &RoleId,
819 privileges: Datum,
820 view: &View,
821 diff: Diff,
822 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
823 let create_stmt = mz_sql::parse::parse(&view.create_sql)
824 .unwrap_or_else(|e| {
825 panic!(
826 "create_sql cannot be invalid: `{}` --- error: `{}`",
827 view.create_sql, e
828 )
829 })
830 .into_element()
831 .ast;
832 let query = match &create_stmt {
833 Statement::CreateView(stmt) => &stmt.definition.query,
834 _ => unreachable!(),
835 };
836
837 let mut query_string = query.to_ast_string_stable();
838 query_string.push(';');
841
842 vec![BuiltinTableUpdate::row(
843 &*MZ_VIEWS,
844 Row::pack_slice(&[
845 Datum::String(&id.to_string()),
846 Datum::UInt32(oid),
847 Datum::String(&schema_id.to_string()),
848 Datum::String(name),
849 Datum::String(&query_string),
850 Datum::String(&owner_id.to_string()),
851 privileges,
852 Datum::String(&view.create_sql),
853 Datum::String(&create_stmt.to_ast_string_redacted()),
854 ]),
855 diff,
856 )]
857 }
858
859 fn pack_materialized_view_update(
860 &self,
861 id: CatalogItemId,
862 mview: &MaterializedView,
863 diff: Diff,
864 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
865 let mut updates = Vec::new();
866
867 if let Some(refresh_schedule) = &mview.refresh_schedule {
868 assert!(!refresh_schedule.is_empty());
871 for RefreshEvery {
872 interval,
873 aligned_to,
874 } in refresh_schedule.everies.iter()
875 {
876 let aligned_to_dt = mz_ore::now::to_datetime(
877 <&Timestamp as TryInto<u64>>::try_into(aligned_to).expect("undoes planning"),
878 );
879 updates.push(BuiltinTableUpdate::row(
880 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
881 Row::pack_slice(&[
882 Datum::String(&id.to_string()),
883 Datum::String("every"),
884 Datum::Interval(
885 Interval::from_duration(interval).expect(
886 "planning ensured that this is convertible back to Interval",
887 ),
888 ),
889 Datum::TimestampTz(aligned_to_dt.try_into().expect("undoes planning")),
890 Datum::Null,
891 ]),
892 diff,
893 ));
894 }
895 for at in refresh_schedule.ats.iter() {
896 let at_dt = mz_ore::now::to_datetime(
897 <&Timestamp as TryInto<u64>>::try_into(at).expect("undoes planning"),
898 );
899 updates.push(BuiltinTableUpdate::row(
900 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
901 Row::pack_slice(&[
902 Datum::String(&id.to_string()),
903 Datum::String("at"),
904 Datum::Null,
905 Datum::Null,
906 Datum::TimestampTz(at_dt.try_into().expect("undoes planning")),
907 ]),
908 diff,
909 ));
910 }
911 } else {
912 updates.push(BuiltinTableUpdate::row(
913 &*MZ_MATERIALIZED_VIEW_REFRESH_STRATEGIES,
914 Row::pack_slice(&[
915 Datum::String(&id.to_string()),
916 Datum::String("on-commit"),
917 Datum::Null,
918 Datum::Null,
919 Datum::Null,
920 ]),
921 diff,
922 ));
923 }
924
925 if let Some(target_id) = mview.replacement_target {
926 updates.push(BuiltinTableUpdate::row(
927 &*MZ_REPLACEMENTS,
928 Row::pack_slice(&[
929 Datum::String(&id.to_string()),
930 Datum::String(&target_id.to_string()),
931 ]),
932 diff,
933 ));
934 }
935
936 updates
937 }
938
939 fn pack_sink_update(
940 &self,
941 id: CatalogItemId,
942 oid: u32,
943 schema_id: &SchemaSpecifier,
944 name: &str,
945 owner_id: &RoleId,
946 sink: &Sink,
947 diff: Diff,
948 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
949 let mut updates = vec![];
950 match &sink.connection {
951 StorageSinkConnection::Kafka(KafkaSinkConnection {
952 topic: topic_name, ..
953 }) => {
954 updates.push(BuiltinTableUpdate::row(
955 &*MZ_KAFKA_SINKS,
956 Row::pack_slice(&[
957 Datum::String(&id.to_string()),
958 Datum::String(topic_name.as_str()),
959 ]),
960 diff,
961 ));
962 }
963 StorageSinkConnection::Iceberg(IcebergSinkConnection {
964 namespace, table, ..
965 }) => {
966 updates.push(BuiltinTableUpdate::row(
967 &*MZ_ICEBERG_SINKS,
968 Row::pack_slice(&[
969 Datum::String(&id.to_string()),
970 Datum::String(namespace.as_str()),
971 Datum::String(table.as_str()),
972 ]),
973 diff,
974 ));
975 }
976 };
977
978 let create_stmt = mz_sql::parse::parse(&sink.create_sql)
979 .unwrap_or_else(|_| panic!("create_sql cannot be invalid: {}", sink.create_sql))
980 .into_element()
981 .ast;
982
983 let envelope = sink.envelope();
984
985 let combined_format = sink.combined_format();
987 let (key_format, value_format) = match sink.formats() {
988 Some((key_format, value_format)) => (key_format, Some(value_format)),
989 None => (None, None),
990 };
991
992 updates.push(BuiltinTableUpdate::row(
993 &*MZ_SINKS,
994 Row::pack_slice(&[
995 Datum::String(&id.to_string()),
996 Datum::UInt32(oid),
997 Datum::String(&schema_id.to_string()),
998 Datum::String(name),
999 Datum::String(sink.connection.name()),
1000 Datum::from(sink.connection_id().map(|id| id.to_string()).as_deref()),
1001 Datum::Null,
1003 Datum::from(envelope),
1004 Datum::from(combined_format.as_ref().map(|f| f.as_ref())),
1007 Datum::from(key_format),
1008 Datum::from(value_format),
1009 Datum::String(&sink.cluster_id.to_string()),
1010 Datum::String(&owner_id.to_string()),
1011 Datum::String(&sink.create_sql),
1012 Datum::String(&create_stmt.to_ast_string_redacted()),
1013 ]),
1014 diff,
1015 ));
1016
1017 updates
1018 }
1019
1020 fn pack_index_update(
1021 &self,
1022 id: CatalogItemId,
1023 index: &Index,
1024 diff: Diff,
1025 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1026 let mut updates = vec![];
1027
1028 let create_stmt = mz_sql::parse::parse(&index.create_sql)
1029 .unwrap_or_else(|e| {
1030 panic!(
1031 "create_sql cannot be invalid: `{}` --- error: `{}`",
1032 index.create_sql, e
1033 )
1034 })
1035 .into_element()
1036 .ast;
1037
1038 let key_sqls = match &create_stmt {
1039 Statement::CreateIndex(CreateIndexStatement { key_parts, .. }) => key_parts
1040 .as_ref()
1041 .expect("key_parts is filled in during planning"),
1042 _ => unreachable!(),
1043 };
1044
1045 let on_entry = self.get_entry_by_global_id(&index.on);
1046 let on_desc = on_entry
1047 .relation_desc()
1048 .expect("can only create indexes on items with a valid description");
1049 let repr_col_types: Vec<ReprColumnType> = on_desc
1050 .typ()
1051 .column_types
1052 .iter()
1053 .map(ReprColumnType::from)
1054 .collect();
1055 for (i, key) in index.keys.iter().enumerate() {
1056 let nullable = key.typ(&repr_col_types).nullable;
1057 let seq_in_index = u64::cast_from(i + 1);
1058 let key_sql = key_sqls
1059 .get(i)
1060 .expect("missing sql information for index key")
1061 .to_ast_string_simple();
1062 let (field_number, expression) = match key {
1063 MirScalarExpr::Column(col, _) => {
1064 (Datum::UInt64(u64::cast_from(*col + 1)), Datum::Null)
1065 }
1066 _ => (Datum::Null, Datum::String(&key_sql)),
1067 };
1068 updates.push(BuiltinTableUpdate::row(
1069 &*MZ_INDEX_COLUMNS,
1070 Row::pack_slice(&[
1071 Datum::String(&id.to_string()),
1072 Datum::UInt64(seq_in_index),
1073 field_number,
1074 expression,
1075 Datum::from(nullable),
1076 ]),
1077 diff,
1078 ));
1079 }
1080
1081 updates
1082 }
1083
1084 fn pack_type_update(
1085 &self,
1086 id: CatalogItemId,
1087 oid: u32,
1088 schema_id: &SchemaSpecifier,
1089 name: &str,
1090 owner_id: &RoleId,
1091 privileges: Datum,
1092 typ: &Type,
1093 diff: Diff,
1094 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1095 let mut out = vec![];
1096
1097 let redacted = typ.create_sql.as_ref().map(|create_sql| {
1098 mz_sql::parse::parse(create_sql)
1099 .unwrap_or_else(|_| panic!("create_sql cannot be invalid: {}", create_sql))
1100 .into_element()
1101 .ast
1102 .to_ast_string_redacted()
1103 });
1104
1105 out.push(BuiltinTableUpdate::row(
1106 &*MZ_TYPES,
1107 Row::pack_slice(&[
1108 Datum::String(&id.to_string()),
1109 Datum::UInt32(oid),
1110 Datum::String(&schema_id.to_string()),
1111 Datum::String(name),
1112 Datum::String(&TypeCategory::from_catalog_type(&typ.details.typ).to_string()),
1113 Datum::String(&owner_id.to_string()),
1114 privileges,
1115 if let Some(create_sql) = &typ.create_sql {
1116 Datum::String(create_sql)
1117 } else {
1118 Datum::Null
1119 },
1120 if let Some(redacted) = &redacted {
1121 Datum::String(redacted)
1122 } else {
1123 Datum::Null
1124 },
1125 ]),
1126 diff,
1127 ));
1128
1129 let mut row = Row::default();
1130 let mut packer = row.packer();
1131
1132 fn append_modifier(packer: &mut RowPacker<'_>, mods: &[i64]) {
1133 if mods.is_empty() {
1134 packer.push(Datum::Null);
1135 } else {
1136 packer.push_list(mods.iter().map(|m| Datum::Int64(*m)));
1137 }
1138 }
1139
1140 let index_id = match &typ.details.typ {
1141 CatalogType::Array {
1142 element_reference: element_id,
1143 } => {
1144 packer.push(Datum::String(&id.to_string()));
1145 packer.push(Datum::String(&element_id.to_string()));
1146 &MZ_ARRAY_TYPES
1147 }
1148 CatalogType::List {
1149 element_reference: element_id,
1150 element_modifiers,
1151 } => {
1152 packer.push(Datum::String(&id.to_string()));
1153 packer.push(Datum::String(&element_id.to_string()));
1154 append_modifier(&mut packer, element_modifiers);
1155 &MZ_LIST_TYPES
1156 }
1157 CatalogType::Map {
1158 key_reference: key_id,
1159 value_reference: value_id,
1160 key_modifiers,
1161 value_modifiers,
1162 } => {
1163 packer.push(Datum::String(&id.to_string()));
1164 packer.push(Datum::String(&key_id.to_string()));
1165 packer.push(Datum::String(&value_id.to_string()));
1166 append_modifier(&mut packer, key_modifiers);
1167 append_modifier(&mut packer, value_modifiers);
1168 &MZ_MAP_TYPES
1169 }
1170 CatalogType::Pseudo => {
1171 packer.push(Datum::String(&id.to_string()));
1172 &MZ_PSEUDO_TYPES
1173 }
1174 _ => {
1175 packer.push(Datum::String(&id.to_string()));
1176 &MZ_BASE_TYPES
1177 }
1178 };
1179 out.push(BuiltinTableUpdate::row(index_id, row, diff));
1180
1181 if let Some(pg_metadata) = &typ.details.pg_metadata {
1182 out.push(BuiltinTableUpdate::row(
1183 &*MZ_TYPE_PG_METADATA,
1184 Row::pack_slice(&[
1185 Datum::String(&id.to_string()),
1186 Datum::UInt32(pg_metadata.typinput_oid),
1187 Datum::UInt32(pg_metadata.typreceive_oid),
1188 ]),
1189 diff,
1190 ));
1191 }
1192
1193 out
1194 }
1195
1196 fn pack_func_update(
1197 &self,
1198 id: CatalogItemId,
1199 schema_id: &SchemaSpecifier,
1200 name: &str,
1201 owner_id: &RoleId,
1202 func: &Func,
1203 diff: Diff,
1204 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1205 let mut updates = vec![];
1206 for func_impl_details in func.inner.func_impls() {
1207 let arg_type_ids = func_impl_details
1208 .arg_typs
1209 .iter()
1210 .map(|typ| self.get_system_type(typ).id().to_string())
1211 .collect::<Vec<_>>();
1212
1213 let mut row = Row::default();
1214 row.packer()
1215 .try_push_array(
1216 &[ArrayDimension {
1217 lower_bound: 1,
1218 length: arg_type_ids.len(),
1219 }],
1220 arg_type_ids.iter().map(|id| Datum::String(id)),
1221 )
1222 .expect(
1223 "arg_type_ids is 1 dimensional, and its length is used for the array length",
1224 );
1225 let arg_type_ids = row.unpack_first();
1226
1227 updates.push(BuiltinTableUpdate::row(
1228 &*MZ_FUNCTIONS,
1229 Row::pack_slice(&[
1230 Datum::String(&id.to_string()),
1231 Datum::UInt32(func_impl_details.oid),
1232 Datum::String(&schema_id.to_string()),
1233 Datum::String(name),
1234 arg_type_ids,
1235 Datum::from(
1236 func_impl_details
1237 .variadic_typ
1238 .map(|typ| self.get_system_type(typ).id().to_string())
1239 .as_deref(),
1240 ),
1241 Datum::from(
1242 func_impl_details
1243 .return_typ
1244 .map(|typ| self.get_system_type(typ).id().to_string())
1245 .as_deref(),
1246 ),
1247 func_impl_details.return_is_set.into(),
1248 Datum::String(&owner_id.to_string()),
1249 ]),
1250 diff,
1251 ));
1252
1253 if let mz_sql::func::Func::Aggregate(_) = func.inner {
1254 updates.push(BuiltinTableUpdate::row(
1255 &*MZ_AGGREGATES,
1256 Row::pack_slice(&[
1257 Datum::UInt32(func_impl_details.oid),
1258 Datum::String("n"),
1260 Datum::Int16(0),
1261 ]),
1262 diff,
1263 ));
1264 }
1265 }
1266 updates
1267 }
1268
1269 pub fn pack_op_update(
1270 &self,
1271 operator: &str,
1272 func_impl_details: FuncImplCatalogDetails,
1273 diff: Diff,
1274 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
1275 let arg_type_ids = func_impl_details
1276 .arg_typs
1277 .iter()
1278 .map(|typ| self.get_system_type(typ).id().to_string())
1279 .collect::<Vec<_>>();
1280
1281 let mut row = Row::default();
1282 row.packer()
1283 .try_push_array(
1284 &[ArrayDimension {
1285 lower_bound: 1,
1286 length: arg_type_ids.len(),
1287 }],
1288 arg_type_ids.iter().map(|id| Datum::String(id)),
1289 )
1290 .expect("arg_type_ids is 1 dimensional, and its length is used for the array length");
1291 let arg_type_ids = row.unpack_first();
1292
1293 BuiltinTableUpdate::row(
1294 &*MZ_OPERATORS,
1295 Row::pack_slice(&[
1296 Datum::UInt32(func_impl_details.oid),
1297 Datum::String(operator),
1298 arg_type_ids,
1299 Datum::from(
1300 func_impl_details
1301 .return_typ
1302 .map(|typ| self.get_system_type(typ).id().to_string())
1303 .as_deref(),
1304 ),
1305 ]),
1306 diff,
1307 )
1308 }
1309
1310 pub fn pack_storage_usage_update(
1311 &self,
1312 VersionedStorageUsage::V1(event): VersionedStorageUsage,
1313 diff: Diff,
1314 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
1315 let id = &MZ_STORAGE_USAGE_BY_SHARD;
1316 let row = Row::pack_slice(&[
1317 Datum::UInt64(event.id),
1318 Datum::from(event.shard_id.as_deref()),
1319 Datum::UInt64(event.size_bytes),
1320 Datum::TimestampTz(
1321 mz_ore::now::to_datetime(event.collection_timestamp)
1322 .try_into()
1323 .expect("must fit"),
1324 ),
1325 ]);
1326 BuiltinTableUpdate::row(id, row, diff)
1327 }
1328
1329 pub fn pack_egress_ip_update(
1330 &self,
1331 ip: &IpNet,
1332 ) -> Result<BuiltinTableUpdate<&'static BuiltinTable>, Error> {
1333 let id = &MZ_EGRESS_IPS;
1334 let addr = ip.network();
1335 let row = Row::pack_slice(&[
1336 Datum::String(&addr.to_string()),
1337 Datum::Int32(ip.prefix_len().into()),
1338 Datum::String(&format!("{}/{}", addr, ip.prefix_len())),
1339 ]);
1340 Ok(BuiltinTableUpdate::row(id, row, Diff::ONE))
1341 }
1342
1343 pub fn pack_license_key_update(
1344 &self,
1345 license_key: &ValidatedLicenseKey,
1346 ) -> Result<BuiltinTableUpdate<&'static BuiltinTable>, Error> {
1347 let id = &MZ_LICENSE_KEYS;
1348 let row = Row::pack_slice(&[
1349 Datum::String(&license_key.id),
1350 Datum::String(&license_key.organization),
1351 Datum::String(&license_key.environment_id),
1352 Datum::TimestampTz(
1353 mz_ore::now::to_datetime(license_key.expiration * 1000)
1354 .try_into()
1355 .expect("must fit"),
1356 ),
1357 Datum::TimestampTz(
1358 mz_ore::now::to_datetime(license_key.not_before * 1000)
1359 .try_into()
1360 .expect("must fit"),
1361 ),
1362 ]);
1363 Ok(BuiltinTableUpdate::row(id, row, Diff::ONE))
1364 }
1365
1366 pub fn pack_all_replica_size_updates(&self) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1367 let mut updates = Vec::new();
1368 for (size, alloc) in &self.cluster_replica_sizes.0 {
1369 let DiskLimit(ByteSize(disk_bytes)) =
1372 (alloc.disk_limit).unwrap_or(DiskLimit::ARBITRARY);
1373
1374 let internal_row = Row::pack_slice(&[
1383 size.as_str().into(),
1384 Datum::from(alloc.swap_enabled),
1385 disk_bytes.into(),
1386 ]);
1387 updates.push(BuiltinTableUpdate::row(
1388 &*MZ_CLUSTER_REPLICA_SIZE_INTERNAL,
1389 internal_row,
1390 Diff::ONE,
1391 ));
1392
1393 if alloc.disabled {
1394 continue;
1395 }
1396
1397 let cpu_limit = alloc.cpu_limit.unwrap_or(CpuLimit::MAX);
1398 let MemoryLimit(ByteSize(memory_bytes)) =
1399 (alloc.memory_limit).unwrap_or(MemoryLimit::MAX);
1400
1401 let row = Row::pack_slice(&[
1402 size.as_str().into(),
1403 u64::cast_from(alloc.scale).into(),
1404 u64::cast_from(alloc.workers).into(),
1405 cpu_limit.as_nanocpus().into(),
1406 memory_bytes.into(),
1407 disk_bytes.into(),
1408 (alloc.credits_per_hour).into(),
1409 ]);
1410
1411 updates.push(BuiltinTableUpdate::row(
1412 &*MZ_CLUSTER_REPLICA_SIZES,
1413 row,
1414 Diff::ONE,
1415 ));
1416 }
1417
1418 updates
1419 }
1420
1421 pub fn pack_subscribe_update(
1422 &self,
1423 id: GlobalId,
1424 subscribe: &ActiveSubscribe,
1425 diff: Diff,
1426 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
1427 let mut row = Row::default();
1428 let mut packer = row.packer();
1429 packer.push(Datum::String(&id.to_string()));
1430 packer.push(Datum::Uuid(subscribe.session_uuid));
1431 packer.push(Datum::String(&subscribe.cluster_id.to_string()));
1432
1433 let start_dt = mz_ore::now::to_datetime(subscribe.start_time);
1434 packer.push(Datum::TimestampTz(start_dt.try_into().expect("must fit")));
1435
1436 let depends_on: Vec<_> = subscribe
1437 .depends_on
1438 .iter()
1439 .map(|id| id.to_string())
1440 .collect();
1441 packer.push_list(depends_on.iter().map(|s| Datum::String(s)));
1442
1443 BuiltinTableUpdate::row(&*MZ_SUBSCRIPTIONS, row, diff)
1444 }
1445
1446 pub fn pack_session_update(
1447 &self,
1448 conn: &ConnMeta,
1449 diff: Diff,
1450 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
1451 let connect_dt = mz_ore::now::to_datetime(conn.connected_at());
1452 BuiltinTableUpdate::row(
1453 &*MZ_SESSIONS,
1454 Row::pack_slice(&[
1455 Datum::Uuid(conn.uuid()),
1456 Datum::UInt32(conn.conn_id().unhandled()),
1457 Datum::String(&conn.authenticated_role_id().to_string()),
1458 Datum::from(conn.client_ip().map(|ip| ip.to_string()).as_deref()),
1459 Datum::TimestampTz(connect_dt.try_into().expect("must fit")),
1460 ]),
1461 diff,
1462 )
1463 }
1464
1465 fn pack_privilege_array_row(&self, privileges: &PrivilegeMap) -> Row {
1466 let mut row = Row::default();
1467 let flat_privileges: Vec<_> = privileges.all_values_owned().collect();
1468 row.packer()
1469 .try_push_array(
1470 &[ArrayDimension {
1471 lower_bound: 1,
1472 length: flat_privileges.len(),
1473 }],
1474 flat_privileges
1475 .into_iter()
1476 .map(|mz_acl_item| Datum::MzAclItem(mz_acl_item.clone())),
1477 )
1478 .expect("privileges is 1 dimensional, and its length is used for the array length");
1479 row
1480 }
1481
1482 pub fn pack_webhook_source_update(
1483 &self,
1484 item_id: CatalogItemId,
1485 diff: Diff,
1486 ) -> BuiltinTableUpdate<&'static BuiltinTable> {
1487 let url = self
1488 .try_get_webhook_url(&item_id)
1489 .expect("webhook source should exist");
1490 let url = url.to_string();
1491 let name = &self.get_entry(&item_id).name().item;
1492 let id_str = item_id.to_string();
1493
1494 BuiltinTableUpdate::row(
1495 &*MZ_WEBHOOKS_SOURCES,
1496 Row::pack_slice(&[
1497 Datum::String(&id_str),
1498 Datum::String(name),
1499 Datum::String(&url),
1500 ]),
1501 diff,
1502 )
1503 }
1504
1505 pub fn pack_source_references_update(
1506 &self,
1507 source_references: &SourceReferences,
1508 diff: Diff,
1509 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1510 let source_id = source_references.source_id.to_string();
1511 let updated_at = &source_references.updated_at;
1512 source_references
1513 .references
1514 .iter()
1515 .map(|reference| {
1516 let mut row = Row::default();
1517 let mut packer = row.packer();
1518 packer.extend([
1519 Datum::String(&source_id),
1520 reference
1521 .namespace
1522 .as_ref()
1523 .map(|s| Datum::String(s))
1524 .unwrap_or(Datum::Null),
1525 Datum::String(&reference.name),
1526 Datum::TimestampTz(
1527 mz_ore::now::to_datetime(*updated_at)
1528 .try_into()
1529 .expect("must fit"),
1530 ),
1531 ]);
1532 if reference.columns.len() > 0 {
1533 packer
1534 .try_push_array(
1535 &[ArrayDimension {
1536 lower_bound: 1,
1537 length: reference.columns.len(),
1538 }],
1539 reference.columns.iter().map(|col| Datum::String(col)),
1540 )
1541 .expect(
1542 "columns is 1 dimensional, and its length is used for the array length",
1543 );
1544 } else {
1545 packer.push(Datum::Null);
1546 }
1547
1548 BuiltinTableUpdate::row(&*MZ_SOURCE_REFERENCES, row, diff)
1549 })
1550 .collect()
1551 }
1552}