1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use anyhow::anyhow;
19use parking_lot::lock_api::ArcRwLockReadGuard;
20use parking_lot::{RawRwLock, RwLock};
21use risingwave_common::catalog::{
22 AlterDatabaseParam, CatalogVersion, FunctionId, IndexId, ObjectId,
23};
24use risingwave_common::id::{ConnectionId, JobId, SchemaId, SourceId, ViewId};
25use risingwave_common::system_param::AdaptiveParallelismStrategy;
26use risingwave_pb::catalog::{
27 PbComment, PbCreateType, PbDatabase, PbFunction, PbIndex, PbSchema, PbSink, PbSource,
28 PbSubscription, PbTable, PbView,
29};
30use risingwave_pb::ddl_service::create_iceberg_table_request::{PbSinkJobInfo, PbTableJobInfo};
31use risingwave_pb::ddl_service::replace_job_plan::{
32 ReplaceJob, ReplaceMaterializedView, ReplaceSink, ReplaceSource, ReplaceTable,
33};
34use risingwave_pb::ddl_service::{
35 PbTableJobType, StreamingJobResourceType, TableJobType, WaitVersion, alter_name_request,
36 alter_owner_request, alter_set_schema_request, alter_swap_rename_request,
37 create_connection_request, streaming_job_resource_type,
38};
39use risingwave_pb::meta::PbTableParallelism;
40use risingwave_pb::stream_plan::StreamFragmentGraph;
41use risingwave_rpc_client::MetaClient;
42use tokio::sync::watch::Receiver;
43
44use super::root_catalog::Catalog;
45use super::{DatabaseId, SecretId, SinkId, SubscriptionId, TableId};
46use crate::error::Result;
47use crate::scheduler::HummockSnapshotManagerRef;
48use crate::session::current::notice_to_user;
49use crate::user::UserId;
50
51pub type CatalogReadGuard = ArcRwLockReadGuard<RawRwLock, Catalog>;
52
53#[derive(Clone)]
55pub struct CatalogReader(Arc<RwLock<Catalog>>);
56
57impl CatalogReader {
58 pub fn new(inner: Arc<RwLock<Catalog>>) -> Self {
59 CatalogReader(inner)
60 }
61
62 pub fn read_guard(&self) -> CatalogReadGuard {
63 self.0.read_arc_recursive()
65 }
66}
67
68#[async_trait::async_trait]
73pub trait CatalogWriter: Send + Sync {
74 async fn create_database(
75 &self,
76 db_name: &str,
77 owner: UserId,
78 resource_group: &str,
79 barrier_interval_ms: Option<u32>,
80 checkpoint_frequency: Option<u64>,
81 ) -> Result<()>;
82
83 async fn create_schema(
84 &self,
85 db_id: DatabaseId,
86 schema_name: &str,
87 owner: UserId,
88 ) -> Result<()>;
89
90 async fn create_view(&self, view: PbView, dependencies: HashSet<ObjectId>) -> Result<()>;
91
92 async fn create_materialized_view(
93 &self,
94 table: PbTable,
95 graph: StreamFragmentGraph,
96 dependencies: HashSet<ObjectId>,
97 resource_type: streaming_job_resource_type::ResourceType,
98 if_not_exists: bool,
99 refresh_interval_sec: Option<u64>,
100 ) -> Result<()>;
101
102 async fn replace_materialized_view(
103 &self,
104 table: PbTable,
105 graph: StreamFragmentGraph,
106 ) -> Result<()>;
107
108 async fn create_table(
109 &self,
110 source: Option<PbSource>,
111 table: PbTable,
112 graph: StreamFragmentGraph,
113 job_type: PbTableJobType,
114 if_not_exists: bool,
115 dependencies: HashSet<ObjectId>,
116 ) -> Result<()>;
117
118 async fn replace_table(
119 &self,
120 source: Option<PbSource>,
121 table: PbTable,
122 graph: StreamFragmentGraph,
123 job_type: TableJobType,
124 ) -> Result<()>;
125
126 async fn replace_source(&self, source: PbSource, graph: StreamFragmentGraph) -> Result<()>;
127
128 async fn create_index(
129 &self,
130 index: PbIndex,
131 table: PbTable,
132 graph: StreamFragmentGraph,
133 resource_type: streaming_job_resource_type::ResourceType,
134 if_not_exists: bool,
135 ) -> Result<()>;
136
137 async fn create_source(
138 &self,
139 source: PbSource,
140 graph: Option<StreamFragmentGraph>,
141 if_not_exists: bool,
142 ) -> Result<()>;
143
144 async fn create_sink(
145 &self,
146 sink: PbSink,
147 graph: StreamFragmentGraph,
148 dependencies: HashSet<ObjectId>,
149 resource_type: streaming_job_resource_type::ResourceType,
150 if_not_exists: bool,
151 since_timestamp_epoch: Option<u64>,
152 ) -> Result<()>;
153
154 async fn replace_sink(
155 &self,
156 old_sink_id: SinkId,
157 sink: PbSink,
158 graph: StreamFragmentGraph,
159 dependencies: HashSet<ObjectId>,
160 resource_type: streaming_job_resource_type::ResourceType,
161 ) -> Result<()>;
162
163 async fn create_subscription(&self, subscription: PbSubscription) -> Result<()>;
164
165 async fn create_function(&self, function: PbFunction) -> Result<()>;
166
167 async fn create_connection(
168 &self,
169 connection_name: String,
170 database_id: DatabaseId,
171 schema_id: SchemaId,
172 owner_id: UserId,
173 connection: create_connection_request::Payload,
174 ) -> Result<()>;
175
176 async fn create_secret(
177 &self,
178 secret_name: String,
179 database_id: DatabaseId,
180 schema_id: SchemaId,
181 owner_id: UserId,
182 payload: Vec<u8>,
183 ) -> Result<()>;
184
185 async fn comment_on(&self, comment: PbComment) -> Result<()>;
186
187 async fn drop_table(
188 &self,
189 source_id: Option<SourceId>,
190 table_id: TableId,
191 cascade: bool,
192 ) -> Result<()>;
193
194 async fn drop_materialized_view(&self, table_id: TableId, cascade: bool) -> Result<()>;
195
196 async fn drop_view(&self, view_id: ViewId, cascade: bool) -> Result<()>;
197
198 async fn drop_source(&self, source_id: SourceId, cascade: bool) -> Result<()>;
199
200 async fn reset_source(&self, source_id: SourceId) -> Result<()>;
201
202 async fn drop_sink(&self, sink_id: SinkId, cascade: bool) -> Result<()>;
203
204 async fn drop_subscription(&self, subscription_id: SubscriptionId, cascade: bool)
205 -> Result<()>;
206
207 async fn drop_database(&self, database_id: DatabaseId) -> Result<()>;
208
209 async fn drop_schema(&self, schema_id: SchemaId, cascade: bool) -> Result<()>;
210
211 async fn drop_index(&self, index_id: IndexId, cascade: bool) -> Result<()>;
212
213 async fn drop_function(&self, function_id: FunctionId, cascade: bool) -> Result<()>;
214
215 async fn drop_connection(&self, connection_id: ConnectionId, cascade: bool) -> Result<()>;
216
217 async fn drop_secret(&self, secret_id: SecretId, cascade: bool) -> Result<()>;
218
219 async fn alter_secret(
220 &self,
221 secret_id: SecretId,
222 secret_name: String,
223 database_id: DatabaseId,
224 schema_id: SchemaId,
225 owner_id: UserId,
226 payload: Vec<u8>,
227 ) -> Result<()>;
228
229 async fn alter_subscription_retention(
230 &self,
231 subscription_id: SubscriptionId,
232 retention_seconds: u64,
233 definition: String,
234 ) -> Result<()>;
235
236 async fn alter_name(
237 &self,
238 object_id: alter_name_request::Object,
239 object_name: &str,
240 ) -> Result<()>;
241
242 async fn alter_owner(
243 &self,
244 object: alter_owner_request::Object,
245 owner_id: UserId,
246 ) -> Result<()>;
247
248 async fn alter_source(&self, source: PbSource) -> Result<()>;
250
251 async fn alter_parallelism(
252 &self,
253 job_id: JobId,
254 parallelism: PbTableParallelism,
255 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
256 deferred: bool,
257 ) -> Result<()>;
258
259 async fn alter_backfill_parallelism(
260 &self,
261 job_id: JobId,
262 parallelism: Option<PbTableParallelism>,
263 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
264 deferred: bool,
265 ) -> Result<()>;
266
267 async fn alter_config(
268 &self,
269 job_id: JobId,
270 entries_to_add: HashMap<String, String>,
271 keys_to_remove: Vec<String>,
272 ) -> Result<()>;
273
274 async fn alter_resource_group(
275 &self,
276 job_id: JobId,
277 resource_group: Option<String>,
278 deferred: bool,
279 ) -> Result<()>;
280
281 async fn alter_database_resource_group(
282 &self,
283 database_id: DatabaseId,
284 resource_group: Option<String>,
285 deferred: bool,
286 ) -> Result<()>;
287
288 async fn alter_set_schema(
289 &self,
290 object: alter_set_schema_request::Object,
291 new_schema_id: SchemaId,
292 ) -> Result<()>;
293
294 async fn alter_swap_rename(&self, object: alter_swap_rename_request::Object) -> Result<()>;
295
296 async fn alter_database_param(
297 &self,
298 database_id: DatabaseId,
299 param: AlterDatabaseParam,
300 ) -> Result<()>;
301
302 async fn create_iceberg_table(
303 &self,
304 table_job_info: PbTableJobInfo,
305 sink_job_info: PbSinkJobInfo,
306 iceberg_source: PbSource,
307 if_not_exists: bool,
308 ) -> Result<()>;
309
310 async fn wait(&self, job_id: Option<JobId>) -> Result<()>;
311}
312
313#[derive(Clone)]
314pub struct CatalogWriterImpl {
315 meta_client: MetaClient,
316 catalog_updated_rx: Receiver<CatalogVersion>,
317 hummock_snapshot_manager: HummockSnapshotManagerRef,
318}
319
320#[async_trait::async_trait]
321impl CatalogWriter for CatalogWriterImpl {
322 async fn create_database(
323 &self,
324 db_name: &str,
325 owner: UserId,
326 resource_group: &str,
327 barrier_interval_ms: Option<u32>,
328 checkpoint_frequency: Option<u64>,
329 ) -> Result<()> {
330 let version = self
331 .meta_client
332 .create_database(PbDatabase {
333 name: db_name.to_owned(),
334 id: 0.into(),
335 owner,
336 resource_group: resource_group.to_owned(),
337 barrier_interval_ms,
338 checkpoint_frequency,
339 })
340 .await?;
341 self.wait_version(version).await
342 }
343
344 async fn create_schema(
345 &self,
346 db_id: DatabaseId,
347 schema_name: &str,
348 owner: UserId,
349 ) -> Result<()> {
350 let version = self
351 .meta_client
352 .create_schema(PbSchema {
353 id: 0.into(),
354 name: schema_name.to_owned(),
355 database_id: db_id,
356 owner,
357 })
358 .await?;
359 self.wait_version(version).await
360 }
361
362 async fn create_materialized_view(
364 &self,
365 table: PbTable,
366 graph: StreamFragmentGraph,
367 dependencies: HashSet<ObjectId>,
368 resource_type: streaming_job_resource_type::ResourceType,
369 if_not_exists: bool,
370 refresh_interval_sec: Option<u64>,
371 ) -> Result<()> {
372 let create_type = table.get_create_type().unwrap_or(PbCreateType::Foreground);
373 let version = self
374 .meta_client
375 .create_materialized_view(
376 table,
377 graph,
378 dependencies,
379 resource_type,
380 if_not_exists,
381 refresh_interval_sec,
382 )
383 .await?;
384 if matches!(create_type, PbCreateType::Foreground) {
385 self.wait_version(version).await?
386 }
387 Ok(())
388 }
389
390 async fn replace_materialized_view(
391 &self,
392 table: PbTable,
393 graph: StreamFragmentGraph,
394 ) -> Result<()> {
395 notice_to_user(format!("table: {table:#?}"));
397 notice_to_user(format!("graph: {graph:#?}"));
398
399 let version = self
400 .meta_client
401 .replace_job(
402 graph,
403 ReplaceJob::ReplaceMaterializedView(ReplaceMaterializedView { table: Some(table) }),
404 )
405 .await?;
406
407 self.wait_version(version).await
408 }
409
410 async fn create_view(&self, view: PbView, dependencies: HashSet<ObjectId>) -> Result<()> {
411 let version = self.meta_client.create_view(view, dependencies).await?;
412 self.wait_version(version).await
413 }
414
415 async fn create_index(
416 &self,
417 index: PbIndex,
418 table: PbTable,
419 graph: StreamFragmentGraph,
420 resource_type: streaming_job_resource_type::ResourceType,
421 if_not_exists: bool,
422 ) -> Result<()> {
423 let version = self
424 .meta_client
425 .create_index(index, table, graph, resource_type, if_not_exists)
426 .await?;
427 self.wait_version(version).await
428 }
429
430 async fn create_table(
431 &self,
432 source: Option<PbSource>,
433 table: PbTable,
434 graph: StreamFragmentGraph,
435 job_type: PbTableJobType,
436 if_not_exists: bool,
437 dependencies: HashSet<ObjectId>,
438 ) -> Result<()> {
439 let version = self
440 .meta_client
441 .create_table(source, table, graph, job_type, if_not_exists, dependencies)
442 .await?;
443 self.wait_version(version).await
444 }
445
446 async fn replace_table(
447 &self,
448 source: Option<PbSource>,
449 table: PbTable,
450 graph: StreamFragmentGraph,
451 job_type: TableJobType,
452 ) -> Result<()> {
453 let version = self
454 .meta_client
455 .replace_job(
456 graph,
457 ReplaceJob::ReplaceTable(ReplaceTable {
458 source,
459 table: Some(table),
460 job_type: job_type as _,
461 }),
462 )
463 .await?;
464 self.wait_version(version).await
465 }
466
467 async fn replace_source(&self, source: PbSource, graph: StreamFragmentGraph) -> Result<()> {
468 let version = self
469 .meta_client
470 .replace_job(
471 graph,
472 ReplaceJob::ReplaceSource(ReplaceSource {
473 source: Some(source),
474 }),
475 )
476 .await?;
477 self.wait_version(version).await
478 }
479
480 async fn create_source(
481 &self,
482 source: PbSource,
483 graph: Option<StreamFragmentGraph>,
484 if_not_exists: bool,
485 ) -> Result<()> {
486 let version = self
487 .meta_client
488 .create_source(source, graph, if_not_exists)
489 .await?;
490 self.wait_version(version).await
491 }
492
493 async fn create_sink(
494 &self,
495 sink: PbSink,
496 graph: StreamFragmentGraph,
497 dependencies: HashSet<ObjectId>,
498 resource_type: streaming_job_resource_type::ResourceType,
499 if_not_exists: bool,
500 since_timestamp_epoch: Option<u64>,
501 ) -> Result<()> {
502 let version = self
503 .meta_client
504 .create_sink(
505 sink,
506 graph,
507 dependencies,
508 resource_type,
509 if_not_exists,
510 since_timestamp_epoch,
511 )
512 .await?;
513 self.wait_version(version).await
514 }
515
516 async fn replace_sink(
517 &self,
518 old_sink_id: SinkId,
519 sink: PbSink,
520 graph: StreamFragmentGraph,
521 dependencies: HashSet<ObjectId>,
522 resource_type: streaming_job_resource_type::ResourceType,
523 ) -> Result<()> {
524 let version = self
525 .meta_client
526 .replace_job(
527 graph,
528 ReplaceJob::ReplaceSink(ReplaceSink {
529 sink: Some(sink),
530 old_sink_id,
531 dependencies: dependencies.into_iter().collect(),
532 resource_type: Some(StreamingJobResourceType {
533 resource_type: Some(resource_type),
534 }),
535 }),
536 )
537 .await?;
538 self.wait_version(version).await
539 }
540
541 async fn create_subscription(&self, subscription: PbSubscription) -> Result<()> {
542 let version = self.meta_client.create_subscription(subscription).await?;
543 self.wait_version(version).await
544 }
545
546 async fn create_function(&self, function: PbFunction) -> Result<()> {
547 let version = self.meta_client.create_function(function).await?;
548 self.wait_version(version).await
549 }
550
551 async fn create_connection(
552 &self,
553 connection_name: String,
554 database_id: DatabaseId,
555 schema_id: SchemaId,
556 owner_id: UserId,
557 connection: create_connection_request::Payload,
558 ) -> Result<()> {
559 let version = self
560 .meta_client
561 .create_connection(
562 connection_name,
563 database_id,
564 schema_id,
565 owner_id,
566 connection,
567 )
568 .await?;
569 self.wait_version(version).await
570 }
571
572 async fn create_secret(
573 &self,
574 secret_name: String,
575 database_id: DatabaseId,
576 schema_id: SchemaId,
577 owner_id: UserId,
578 payload: Vec<u8>,
579 ) -> Result<()> {
580 let version = self
581 .meta_client
582 .create_secret(secret_name, database_id, schema_id, owner_id, payload)
583 .await?;
584 self.wait_version(version).await
585 }
586
587 async fn comment_on(&self, comment: PbComment) -> Result<()> {
588 let version = self.meta_client.comment_on(comment).await?;
589 self.wait_version(version).await
590 }
591
592 async fn drop_table(
593 &self,
594 source_id: Option<SourceId>,
595 table_id: TableId,
596 cascade: bool,
597 ) -> Result<()> {
598 let version = self
599 .meta_client
600 .drop_table(source_id, table_id, cascade)
601 .await?;
602 self.wait_version(version).await
603 }
604
605 async fn drop_materialized_view(&self, table_id: TableId, cascade: bool) -> Result<()> {
606 let version = self
607 .meta_client
608 .drop_materialized_view(table_id, cascade)
609 .await?;
610 self.wait_version(version).await
611 }
612
613 async fn drop_view(&self, view_id: ViewId, cascade: bool) -> Result<()> {
614 let version = self.meta_client.drop_view(view_id, cascade).await?;
615 self.wait_version(version).await
616 }
617
618 async fn drop_source(&self, source_id: SourceId, cascade: bool) -> Result<()> {
619 let version = self.meta_client.drop_source(source_id, cascade).await?;
620 self.wait_version(version).await
621 }
622
623 async fn reset_source(&self, source_id: SourceId) -> Result<()> {
624 let version = self.meta_client.reset_source(source_id).await?;
625 self.wait_version(version).await
626 }
627
628 async fn drop_sink(&self, sink_id: SinkId, cascade: bool) -> Result<()> {
629 let version = self.meta_client.drop_sink(sink_id, cascade).await?;
630 self.wait_version(version).await
631 }
632
633 async fn drop_subscription(
634 &self,
635 subscription_id: SubscriptionId,
636 cascade: bool,
637 ) -> Result<()> {
638 let version = self
639 .meta_client
640 .drop_subscription(subscription_id, cascade)
641 .await?;
642 self.wait_version(version).await
643 }
644
645 async fn drop_index(&self, index_id: IndexId, cascade: bool) -> Result<()> {
646 let version = self.meta_client.drop_index(index_id, cascade).await?;
647 self.wait_version(version).await
648 }
649
650 async fn drop_function(&self, function_id: FunctionId, cascade: bool) -> Result<()> {
651 let version = self.meta_client.drop_function(function_id, cascade).await?;
652 self.wait_version(version).await
653 }
654
655 async fn drop_schema(&self, schema_id: SchemaId, cascade: bool) -> Result<()> {
656 let version = self.meta_client.drop_schema(schema_id, cascade).await?;
657 self.wait_version(version).await
658 }
659
660 async fn drop_database(&self, database_id: DatabaseId) -> Result<()> {
661 let version = self.meta_client.drop_database(database_id).await?;
662 self.wait_version(version).await
663 }
664
665 async fn drop_connection(&self, connection_id: ConnectionId, cascade: bool) -> Result<()> {
666 let version = self
667 .meta_client
668 .drop_connection(connection_id, cascade)
669 .await?;
670 self.wait_version(version).await
671 }
672
673 async fn drop_secret(&self, secret_id: SecretId, cascade: bool) -> Result<()> {
674 let version = self.meta_client.drop_secret(secret_id, cascade).await?;
675 self.wait_version(version).await
676 }
677
678 async fn alter_name(
679 &self,
680 object_id: alter_name_request::Object,
681 object_name: &str,
682 ) -> Result<()> {
683 let version = self.meta_client.alter_name(object_id, object_name).await?;
684 self.wait_version(version).await
685 }
686
687 async fn alter_owner(
688 &self,
689 object: alter_owner_request::Object,
690 owner_id: UserId,
691 ) -> Result<()> {
692 let version = self.meta_client.alter_owner(object, owner_id).await?;
693 self.wait_version(version).await
694 }
695
696 async fn alter_set_schema(
697 &self,
698 object: alter_set_schema_request::Object,
699 new_schema_id: SchemaId,
700 ) -> Result<()> {
701 let version = self
702 .meta_client
703 .alter_set_schema(object, new_schema_id)
704 .await?;
705 self.wait_version(version).await
706 }
707
708 async fn alter_source(&self, source: PbSource) -> Result<()> {
709 let version = self.meta_client.alter_source(source).await?;
710 self.wait_version(version).await
711 }
712
713 async fn alter_parallelism(
714 &self,
715 job_id: JobId,
716 parallelism: PbTableParallelism,
717 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
718 deferred: bool,
719 ) -> Result<()> {
720 self.meta_client
721 .alter_parallelism(job_id, parallelism, adaptive_parallelism_strategy, deferred)
722 .await?;
723 Ok(())
724 }
725
726 async fn alter_backfill_parallelism(
727 &self,
728 job_id: JobId,
729 parallelism: Option<PbTableParallelism>,
730 adaptive_parallelism_strategy: Option<AdaptiveParallelismStrategy>,
731 deferred: bool,
732 ) -> Result<()> {
733 self.meta_client
734 .alter_backfill_parallelism(
735 job_id,
736 parallelism,
737 adaptive_parallelism_strategy,
738 deferred,
739 )
740 .await?;
741 Ok(())
742 }
743
744 async fn alter_config(
745 &self,
746 job_id: JobId,
747 entries_to_add: HashMap<String, String>,
748 keys_to_remove: Vec<String>,
749 ) -> Result<()> {
750 self.meta_client
751 .alter_streaming_job_config(job_id, entries_to_add, keys_to_remove)
752 .await?;
753 Ok(())
754 }
755
756 async fn alter_swap_rename(&self, object: alter_swap_rename_request::Object) -> Result<()> {
757 let version = self.meta_client.alter_swap_rename(object).await?;
758 self.wait_version(version).await
759 }
760
761 async fn alter_secret(
762 &self,
763 secret_id: SecretId,
764 secret_name: String,
765 database_id: DatabaseId,
766 schema_id: SchemaId,
767 owner_id: UserId,
768 payload: Vec<u8>,
769 ) -> Result<()> {
770 let version = self
771 .meta_client
772 .alter_secret(
773 secret_id,
774 secret_name,
775 database_id,
776 schema_id,
777 owner_id,
778 payload,
779 )
780 .await?;
781 self.wait_version(version).await
782 }
783
784 async fn alter_subscription_retention(
785 &self,
786 subscription_id: SubscriptionId,
787 retention_seconds: u64,
788 definition: String,
789 ) -> Result<()> {
790 let version = self
791 .meta_client
792 .alter_subscription_retention(subscription_id, retention_seconds, definition)
793 .await?;
794 self.wait_version(version).await
795 }
796
797 async fn alter_resource_group(
798 &self,
799 job_id: JobId,
800 resource_group: Option<String>,
801 deferred: bool,
802 ) -> Result<()> {
803 self.meta_client
804 .alter_resource_group(job_id, resource_group, deferred)
805 .await
806 .map_err(|e| anyhow!(e))?;
807
808 Ok(())
809 }
810
811 async fn alter_database_resource_group(
812 &self,
813 database_id: DatabaseId,
814 resource_group: Option<String>,
815 deferred: bool,
816 ) -> Result<()> {
817 let version = self
818 .meta_client
819 .alter_database_resource_group(database_id, resource_group, deferred)
820 .await
821 .map_err(|e| anyhow!(e))?;
822 self.wait_version(version).await
823 }
824
825 async fn alter_database_param(
826 &self,
827 database_id: DatabaseId,
828 param: AlterDatabaseParam,
829 ) -> Result<()> {
830 let version = self
831 .meta_client
832 .alter_database_param(database_id, param)
833 .await
834 .map_err(|e| anyhow!(e))?;
835 self.wait_version(version).await
836 }
837
838 async fn create_iceberg_table(
839 &self,
840 table_job_info: PbTableJobInfo,
841 sink_job_info: PbSinkJobInfo,
842 iceberg_source: PbSource,
843 if_not_exists: bool,
844 ) -> Result<()> {
845 let version = Box::pin(self.meta_client.create_iceberg_table(
846 table_job_info,
847 sink_job_info,
848 iceberg_source,
849 if_not_exists,
850 ))
851 .await?;
852 self.wait_version(version).await
853 }
854
855 async fn wait(&self, job_id: Option<JobId>) -> Result<()> {
856 let version = self
857 .meta_client
858 .wait(job_id)
859 .await
860 .map_err(|e| anyhow!(e))?;
861 self.wait_version(version).await
862 }
863}
864
865impl CatalogWriterImpl {
866 pub fn new(
867 meta_client: MetaClient,
868 catalog_updated_rx: Receiver<CatalogVersion>,
869 hummock_snapshot_manager: HummockSnapshotManagerRef,
870 ) -> Self {
871 Self {
872 meta_client,
873 catalog_updated_rx,
874 hummock_snapshot_manager,
875 }
876 }
877
878 async fn wait_version(&self, version: WaitVersion) -> Result<()> {
879 let mut rx = self.catalog_updated_rx.clone();
880 while *rx.borrow_and_update() < version.catalog_version {
881 rx.changed().await.map_err(|e| anyhow!(e))?;
882 }
883 self.hummock_snapshot_manager
884 .wait(version.hummock_version_id)
885 .await;
886 Ok(())
887 }
888}