Skip to main content

risingwave_frontend/catalog/
root_catalog.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use itertools::Itertools;
19use risingwave_common::catalog::{FunctionId, IndexId, StreamJobStatus, TableId};
20use risingwave_common::id::ObjectId;
21use risingwave_common::session_config::{SearchPath, USER_NAME_WILD_CARD};
22use risingwave_common::types::DataType;
23use risingwave_connector::sink::catalog::SinkCatalog;
24use risingwave_pb::catalog::{
25    PbConnection, PbDatabase, PbFunction, PbIndex, PbSchema, PbSecret, PbSink, PbSource,
26    PbSubscription, PbTable, PbView,
27};
28use risingwave_pb::common::PbObjectType;
29use risingwave_pb::hummock::HummockVersionStats;
30use risingwave_pb::meta::ObjectDependency as PbObjectDependency;
31
32use super::function_catalog::FunctionCatalog;
33use super::source_catalog::SourceCatalog;
34use super::subscription_catalog::{SubscriptionCatalog, SubscriptionState};
35use super::view_catalog::ViewCatalog;
36use super::{
37    CatalogError, CatalogResult, ConnectionId, SecretId, SinkId, SourceId, SubscriptionId, ViewId,
38};
39use crate::catalog::connection_catalog::ConnectionCatalog;
40use crate::catalog::database_catalog::DatabaseCatalog;
41use crate::catalog::schema_catalog::SchemaCatalog;
42use crate::catalog::secret_catalog::SecretCatalog;
43use crate::catalog::system_catalog::{
44    SystemTableCatalog, get_sys_tables_in_schema, get_sys_views_in_schema,
45};
46use crate::catalog::table_catalog::TableCatalog;
47use crate::catalog::{DatabaseId, IndexCatalog, SchemaId};
48use crate::expr::{Expr, ExprImpl};
49
50#[derive(Copy, Clone)]
51pub enum SchemaPath<'a> {
52    Name(&'a str),
53    /// (`search_path`, `user_name`).
54    Path(&'a SearchPath, &'a str),
55}
56
57impl<'a> SchemaPath<'a> {
58    pub fn new(
59        schema_name: Option<&'a str>,
60        search_path: &'a SearchPath,
61        user_name: &'a str,
62    ) -> Self {
63        match schema_name {
64            Some(schema_name) => SchemaPath::Name(schema_name),
65            None => SchemaPath::Path(search_path, user_name),
66        }
67    }
68
69    /// Call function `f` for each schema name. Return the first `Some` result.
70    pub fn try_find<T, E>(
71        &self,
72        mut f: impl FnMut(&str) -> Result<Option<T>, E>,
73    ) -> Result<Option<(T, &'a str)>, E> {
74        match self {
75            SchemaPath::Name(schema_name) => Ok(f(schema_name)?.map(|t| (t, *schema_name))),
76            SchemaPath::Path(search_path, user_name) => {
77                for schema_name in search_path.path() {
78                    let mut schema_name: &str = schema_name;
79                    if schema_name == USER_NAME_WILD_CARD {
80                        schema_name = user_name;
81                    }
82                    if let Ok(Some(res)) = f(schema_name) {
83                        return Ok(Some((res, schema_name)));
84                    }
85                }
86                Ok(None)
87            }
88        }
89    }
90}
91
92#[derive(Clone, Debug)]
93pub struct ObjectDependency {
94    pub object_id: ObjectId,
95    pub referenced_object_id: ObjectId,
96    pub referenced_object_type: PbObjectType,
97}
98
99impl From<PbObjectDependency> for ObjectDependency {
100    fn from(dependency: PbObjectDependency) -> Self {
101        let referenced_object_type = PbObjectType::try_from(dependency.referenced_object_type)
102            .unwrap_or(PbObjectType::Unspecified);
103        Self {
104            object_id: dependency.object_id,
105            referenced_object_id: dependency.referenced_object_id,
106            referenced_object_type,
107        }
108    }
109}
110
111/// Root catalog of database catalog. It manages all database/schema/table in memory on frontend.
112/// It is protected by a `RwLock`. Only [`crate::observer::FrontendObserverNode`]
113/// will acquire the write lock and sync it with the meta catalog. In other situations, it is
114/// read only.
115///
116/// - catalog (root catalog)
117///   - database catalog
118///     - schema catalog
119///       - function catalog (i.e., user defined function)
120///       - table/sink/source/index/view catalog
121///        - column catalog
122pub struct Catalog {
123    database_by_name: HashMap<String, DatabaseCatalog>,
124    db_name_by_id: HashMap<DatabaseId, String>,
125    /// all table catalogs in the cluster identified by universal unique table id.
126    table_by_id: HashMap<TableId, Arc<TableCatalog>>,
127    table_stats: HummockVersionStats,
128    object_dependencies: HashMap<ObjectId, Vec<ObjectDependency>>,
129}
130
131#[expect(clippy::derivable_impls)]
132impl Default for Catalog {
133    fn default() -> Self {
134        Self {
135            database_by_name: HashMap::new(),
136            db_name_by_id: HashMap::new(),
137            table_by_id: HashMap::new(),
138            table_stats: HummockVersionStats::default(),
139            object_dependencies: HashMap::new(),
140        }
141    }
142}
143
144impl Catalog {
145    fn get_database_mut(&mut self, db_id: DatabaseId) -> Option<&mut DatabaseCatalog> {
146        let name = self.db_name_by_id.get(&db_id)?;
147        self.database_by_name.get_mut(name)
148    }
149
150    pub fn clear(&mut self) {
151        self.database_by_name.clear();
152        self.db_name_by_id.clear();
153        self.table_by_id.clear();
154        self.object_dependencies.clear();
155    }
156
157    pub fn create_database(&mut self, db: &PbDatabase) {
158        let name = db.name.clone();
159        let id = db.id;
160
161        self.database_by_name
162            .try_insert(name.clone(), db.into())
163            .unwrap();
164        self.db_name_by_id.try_insert(id, name).unwrap();
165    }
166
167    pub fn create_schema(&mut self, proto: &PbSchema) {
168        let database_id = proto.database_id;
169        let id = proto.id;
170        self.get_database_mut(database_id)
171            .unwrap()
172            .create_schema(proto);
173
174        for sys_table in get_sys_tables_in_schema(proto.name.as_str()) {
175            self.get_database_mut(database_id)
176                .unwrap()
177                .get_schema_mut(id)
178                .unwrap()
179                .create_sys_table(sys_table);
180        }
181        for mut sys_view in get_sys_views_in_schema(proto.name.as_str()) {
182            sys_view.database_id = database_id;
183            sys_view.schema_id = id;
184            self.get_database_mut(database_id)
185                .unwrap()
186                .get_schema_mut(id)
187                .unwrap()
188                .create_sys_view(Arc::new(sys_view));
189        }
190    }
191
192    pub fn create_table(&mut self, proto: &PbTable) {
193        let table = self
194            .get_database_mut(proto.database_id)
195            .unwrap()
196            .get_schema_mut(proto.schema_id)
197            .unwrap()
198            .create_table(proto);
199        self.table_by_id.insert(proto.id, table);
200    }
201
202    pub fn create_index(&mut self, proto: &PbIndex) {
203        self.get_database_mut(proto.database_id)
204            .unwrap()
205            .get_schema_mut(proto.schema_id)
206            .unwrap()
207            .create_index(proto);
208    }
209
210    pub fn create_source(&mut self, proto: &PbSource) {
211        self.get_database_mut(proto.database_id)
212            .unwrap()
213            .get_schema_mut(proto.schema_id)
214            .unwrap()
215            .create_source(proto);
216    }
217
218    pub fn create_sink(&mut self, proto: &PbSink) {
219        self.get_database_mut(proto.database_id)
220            .unwrap()
221            .get_schema_mut(proto.schema_id)
222            .unwrap()
223            .create_sink(proto);
224    }
225
226    pub fn create_subscription(&mut self, proto: &PbSubscription) {
227        self.get_database_mut(proto.database_id)
228            .unwrap()
229            .get_schema_mut(proto.schema_id)
230            .unwrap()
231            .create_subscription(proto);
232    }
233
234    pub fn create_secret(&mut self, proto: &PbSecret) {
235        self.get_database_mut(proto.database_id)
236            .unwrap()
237            .get_schema_mut(proto.schema_id)
238            .unwrap()
239            .create_secret(proto);
240    }
241
242    pub fn create_view(&mut self, proto: &PbView) {
243        self.get_database_mut(proto.database_id)
244            .unwrap()
245            .get_schema_mut(proto.schema_id)
246            .unwrap()
247            .create_view(proto);
248    }
249
250    pub fn create_function(&mut self, proto: &PbFunction) {
251        self.get_database_mut(proto.database_id)
252            .unwrap()
253            .get_schema_mut(proto.schema_id)
254            .unwrap()
255            .create_function(proto);
256    }
257
258    pub fn create_connection(&mut self, proto: &PbConnection) {
259        self.get_database_mut(proto.database_id)
260            .unwrap()
261            .get_schema_mut(proto.schema_id)
262            .unwrap()
263            .create_connection(proto);
264    }
265
266    pub fn drop_connection(
267        &mut self,
268        db_id: DatabaseId,
269        schema_id: SchemaId,
270        connection_id: ConnectionId,
271    ) {
272        self.get_database_mut(db_id)
273            .unwrap()
274            .get_schema_mut(schema_id)
275            .unwrap()
276            .drop_connection(connection_id);
277    }
278
279    pub fn update_connection(&mut self, proto: &PbConnection) {
280        let database = self.get_database_mut(proto.database_id).unwrap();
281        let schema = database.get_schema_mut(proto.schema_id).unwrap();
282        if schema.get_connection_by_id(proto.id).is_some() {
283            schema.update_connection(proto);
284        } else {
285            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
286            schema.create_connection(proto);
287            database
288                .iter_schemas_mut()
289                .find(|schema| {
290                    schema.id() != proto.schema_id
291                        && schema.get_connection_by_id(proto.id).is_some()
292                })
293                .unwrap()
294                .drop_connection(proto.id);
295        }
296    }
297
298    pub fn update_secret(&mut self, proto: &PbSecret) {
299        let database = self.get_database_mut(proto.database_id).unwrap();
300        let schema = database.get_schema_mut(proto.schema_id).unwrap();
301        let secret_id = proto.id;
302        if schema.get_secret_by_id(secret_id).is_some() {
303            schema.update_secret(proto);
304        } else {
305            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
306            schema.create_secret(proto);
307            database
308                .iter_schemas_mut()
309                .find(|schema| {
310                    schema.id() != proto.schema_id && schema.get_secret_by_id(secret_id).is_some()
311                })
312                .unwrap()
313                .drop_secret(secret_id);
314        }
315    }
316
317    pub fn drop_database(&mut self, db_id: DatabaseId) {
318        let name = self.db_name_by_id.remove(&db_id).unwrap();
319        let database = self.database_by_name.remove(&name).unwrap();
320        let object_ids: HashSet<ObjectId> = database.iter_object_ids().collect();
321        self.remove_object_dependencies_for_objects(&object_ids);
322        database.iter_all_table_ids().for_each(|table| {
323            self.table_by_id.remove(&table);
324        });
325    }
326
327    pub fn drop_schema(&mut self, db_id: DatabaseId, schema_id: SchemaId) {
328        let object_ids = self
329            .get_database_by_id(db_id)
330            .ok()
331            .and_then(|db| db.get_schema_by_id(schema_id))
332            .map(|schema| schema.iter_object_ids().collect::<HashSet<_>>())
333            .unwrap_or_default();
334        self.remove_object_dependencies_for_objects(&object_ids);
335        self.get_database_mut(db_id).unwrap().drop_schema(schema_id);
336    }
337
338    pub fn drop_table(&mut self, db_id: DatabaseId, schema_id: SchemaId, tb_id: TableId) {
339        self.remove_object_dependencies_for_object(tb_id.as_object_id());
340        self.table_by_id.remove(&tb_id);
341        self.get_database_mut(db_id)
342            .unwrap()
343            .get_schema_mut(schema_id)
344            .unwrap()
345            .drop_table(tb_id);
346    }
347
348    pub fn update_table(&mut self, proto: &PbTable) {
349        let database = self.get_database_mut(proto.database_id).unwrap();
350        let schema = database.get_schema_mut(proto.schema_id).unwrap();
351        let table = if schema.get_table_by_id(proto.id).is_some() {
352            schema.update_table(proto)
353        } else {
354            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
355            let new_table = schema.create_table(proto);
356            database
357                .iter_schemas_mut()
358                .find(|schema| {
359                    schema.id() != proto.schema_id
360                        && schema.get_created_table_by_id(proto.id).is_some()
361                })
362                .unwrap()
363                .drop_table(proto.id);
364            new_table
365        };
366
367        self.table_by_id.insert(proto.id, table);
368    }
369
370    pub fn update_database(&mut self, proto: &PbDatabase) {
371        let id = proto.id;
372        let name = proto.name.clone();
373
374        let old_database_name = self.db_name_by_id.get(&id).unwrap().to_owned();
375        if old_database_name != name {
376            let mut database = self.database_by_name.remove(&old_database_name).unwrap();
377            database.name.clone_from(&name);
378            database.owner = proto.owner;
379            database.resource_group.clone_from(&proto.resource_group);
380            database.barrier_interval_ms = proto.barrier_interval_ms;
381            database.checkpoint_frequency = proto.checkpoint_frequency;
382            self.database_by_name.insert(name.clone(), database);
383            self.db_name_by_id.insert(id, name);
384        } else {
385            let database = self.get_database_mut(id).unwrap();
386            database.name = name;
387            database.owner = proto.owner;
388            database.resource_group = proto.resource_group.clone();
389            database.barrier_interval_ms = proto.barrier_interval_ms;
390            database.checkpoint_frequency = proto.checkpoint_frequency;
391        }
392    }
393
394    pub fn update_schema(&mut self, proto: &PbSchema) {
395        self.get_database_mut(proto.database_id)
396            .unwrap()
397            .update_schema(proto);
398    }
399
400    pub fn update_index(&mut self, proto: &PbIndex) {
401        let database = self.get_database_mut(proto.database_id).unwrap();
402        let schema = database.get_schema_mut(proto.schema_id).unwrap();
403        if schema.get_index_by_id(proto.id).is_some() {
404            schema.update_index(proto);
405        } else {
406            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
407            schema.create_index(proto);
408            database
409                .iter_schemas_mut()
410                .find(|schema| {
411                    schema.id() != proto.schema_id && schema.get_index_by_id(proto.id).is_some()
412                })
413                .unwrap()
414                .drop_index(proto.id);
415        }
416    }
417
418    pub fn drop_source(&mut self, db_id: DatabaseId, schema_id: SchemaId, source_id: SourceId) {
419        self.remove_object_dependencies_for_object(source_id.as_object_id());
420        self.get_database_mut(db_id)
421            .unwrap()
422            .get_schema_mut(schema_id)
423            .unwrap()
424            .drop_source(source_id);
425    }
426
427    pub fn update_source(&mut self, proto: &PbSource) {
428        let database = self.get_database_mut(proto.database_id).unwrap();
429        let schema = database.get_schema_mut(proto.schema_id).unwrap();
430        if schema.get_source_by_id(proto.id).is_some() {
431            schema.update_source(proto);
432        } else {
433            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
434            schema.create_source(proto);
435            database
436                .iter_schemas_mut()
437                .find(|schema| {
438                    schema.id() != proto.schema_id && schema.get_source_by_id(proto.id).is_some()
439                })
440                .unwrap()
441                .drop_source(proto.id);
442        }
443    }
444
445    pub fn drop_sink(&mut self, db_id: DatabaseId, schema_id: SchemaId, sink_id: SinkId) {
446        self.remove_object_dependencies_for_object(sink_id.as_object_id());
447        self.get_database_mut(db_id)
448            .unwrap()
449            .get_schema_mut(schema_id)
450            .unwrap()
451            .drop_sink(sink_id);
452    }
453
454    pub fn drop_secret(&mut self, db_id: DatabaseId, schema_id: SchemaId, secret_id: SecretId) {
455        self.remove_object_dependencies_for_object(secret_id.as_object_id());
456        self.get_database_mut(db_id)
457            .unwrap()
458            .get_schema_mut(schema_id)
459            .unwrap()
460            .drop_secret(secret_id);
461    }
462
463    pub fn update_sink(&mut self, proto: &PbSink) {
464        let database = self.get_database_mut(proto.database_id).unwrap();
465        let schema = database.get_schema_mut(proto.schema_id).unwrap();
466        if schema.get_sink_by_id(proto.id).is_some() {
467            schema.update_sink(proto);
468        } else {
469            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
470            schema.create_sink(proto);
471            database
472                .iter_schemas_mut()
473                .find(|schema| {
474                    schema.id() != proto.schema_id && schema.get_sink_by_id(proto.id).is_some()
475                })
476                .unwrap()
477                .drop_sink(proto.id);
478        }
479    }
480
481    pub fn drop_subscription(
482        &mut self,
483        db_id: DatabaseId,
484        schema_id: SchemaId,
485        subscription_id: SubscriptionId,
486    ) {
487        self.remove_object_dependencies_for_object(subscription_id.as_object_id());
488        self.get_database_mut(db_id)
489            .unwrap()
490            .get_schema_mut(schema_id)
491            .unwrap()
492            .drop_subscription(subscription_id);
493    }
494
495    pub fn update_subscription(&mut self, proto: &PbSubscription) {
496        let database = self.get_database_mut(proto.database_id).unwrap();
497        let schema = database.get_schema_mut(proto.schema_id).unwrap();
498        if schema.get_subscription_by_id(proto.id).is_some() {
499            schema.update_subscription(proto);
500        } else {
501            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
502            schema.create_subscription(proto);
503            database
504                .iter_schemas_mut()
505                .find(|schema| {
506                    schema.id() != proto.schema_id
507                        && schema.get_subscription_by_id(proto.id).is_some()
508                })
509                .unwrap()
510                .drop_subscription(proto.id);
511        }
512    }
513
514    pub fn drop_index(&mut self, db_id: DatabaseId, schema_id: SchemaId, index_id: IndexId) {
515        self.remove_object_dependencies_for_object(index_id.as_object_id());
516        self.get_database_mut(db_id)
517            .unwrap()
518            .get_schema_mut(schema_id)
519            .unwrap()
520            .drop_index(index_id);
521    }
522
523    pub fn drop_view(&mut self, db_id: DatabaseId, schema_id: SchemaId, view_id: ViewId) {
524        self.remove_object_dependencies_for_object(view_id.as_object_id());
525        self.get_database_mut(db_id)
526            .unwrap()
527            .get_schema_mut(schema_id)
528            .unwrap()
529            .drop_view(view_id);
530    }
531
532    pub fn update_view(&mut self, proto: &PbView) {
533        let database = self.get_database_mut(proto.database_id).unwrap();
534        let schema = database.get_schema_mut(proto.schema_id).unwrap();
535        if schema.get_view_by_id(proto.id).is_some() {
536            schema.update_view(proto);
537        } else {
538            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
539            schema.create_view(proto);
540            database
541                .iter_schemas_mut()
542                .find(|schema| {
543                    schema.id() != proto.schema_id && schema.get_view_by_id(proto.id).is_some()
544                })
545                .unwrap()
546                .drop_view(proto.id);
547        }
548    }
549
550    pub fn drop_function(
551        &mut self,
552        db_id: DatabaseId,
553        schema_id: SchemaId,
554        function_id: FunctionId,
555    ) {
556        self.remove_object_dependencies_for_object(function_id.as_object_id());
557        self.get_database_mut(db_id)
558            .unwrap()
559            .get_schema_mut(schema_id)
560            .unwrap()
561            .drop_function(function_id);
562    }
563
564    pub fn update_function(&mut self, proto: &PbFunction) {
565        let database = self.get_database_mut(proto.database_id).unwrap();
566        let schema = database.get_schema_mut(proto.schema_id).unwrap();
567        if schema.get_function_by_id(proto.id).is_some() {
568            schema.update_function(proto);
569        } else {
570            // Enter this branch when schema is changed by `ALTER ... SET SCHEMA ...` statement.
571            schema.create_function(proto);
572            database
573                .iter_schemas_mut()
574                .find(|schema| {
575                    schema.id() != proto.schema_id && schema.get_function_by_id(proto.id).is_some()
576                })
577                .unwrap()
578                .drop_function(proto.id);
579        }
580
581        self.get_database_mut(proto.database_id)
582            .unwrap()
583            .get_schema_mut(proto.schema_id)
584            .unwrap()
585            .update_function(proto);
586    }
587
588    pub fn get_database_by_name(&self, db_name: &str) -> CatalogResult<&DatabaseCatalog> {
589        self.database_by_name
590            .get(db_name)
591            .ok_or_else(|| CatalogError::not_found("database", db_name))
592    }
593
594    pub fn get_database_by_id(&self, db_id: DatabaseId) -> CatalogResult<&DatabaseCatalog> {
595        let db_name = self
596            .db_name_by_id
597            .get(&db_id)
598            .ok_or_else(|| CatalogError::not_found("db_id", db_id.to_string()))?;
599        self.database_by_name
600            .get(db_name)
601            .ok_or_else(|| CatalogError::not_found("database", db_name))
602    }
603
604    pub fn find_schema_secret_by_secret_id(
605        &self,
606        db_name: &str,
607        secret_id: SecretId,
608    ) -> CatalogResult<(String, String)> {
609        let db = self.get_database_by_name(db_name)?;
610        let schema_secret = db
611            .iter_schemas()
612            .find_map(|schema| {
613                schema
614                    .get_secret_by_id(secret_id)
615                    .map(|secret| (schema.name(), secret.name.clone()))
616            })
617            .ok_or_else(|| CatalogError::not_found("secret", secret_id.to_string()))?;
618        Ok(schema_secret)
619    }
620
621    pub fn get_all_schema_names(&self, db_name: &str) -> CatalogResult<Vec<String>> {
622        Ok(self.get_database_by_name(db_name)?.get_all_schema_names())
623    }
624
625    pub fn iter_schemas(
626        &self,
627        db_name: &str,
628    ) -> CatalogResult<impl Iterator<Item = &SchemaCatalog>> {
629        Ok(self.get_database_by_name(db_name)?.iter_schemas())
630    }
631
632    pub fn get_all_database_names(&self) -> Vec<String> {
633        self.database_by_name.keys().cloned().collect_vec()
634    }
635
636    pub fn iter_databases(&self) -> impl Iterator<Item = &DatabaseCatalog> {
637        self.database_by_name.values()
638    }
639
640    pub fn get_schema_by_name(
641        &self,
642        db_name: &str,
643        schema_name: &str,
644    ) -> CatalogResult<&SchemaCatalog> {
645        self.get_database_by_name(db_name)?
646            .get_schema_by_name(schema_name)
647            .ok_or_else(|| CatalogError::not_found("schema", schema_name))
648    }
649
650    pub fn get_table_name_by_id(&self, table_id: TableId) -> CatalogResult<String> {
651        self.get_any_table_by_id(table_id)
652            .map(|table| table.name.clone())
653    }
654
655    pub fn get_schema_by_id(
656        &self,
657        db_id: DatabaseId,
658        schema_id: SchemaId,
659    ) -> CatalogResult<&SchemaCatalog> {
660        self.get_database_by_id(db_id)?
661            .get_schema_by_id(schema_id)
662            .ok_or_else(|| CatalogError::not_found("schema_id", schema_id.to_string()))
663    }
664
665    /// Refer to [`SearchPath`].
666    pub fn first_valid_schema(
667        &self,
668        db_name: &str,
669        search_path: &SearchPath,
670        user_name: &str,
671    ) -> CatalogResult<&SchemaCatalog> {
672        for path in search_path.real_path() {
673            let mut schema_name: &str = path;
674            if schema_name == USER_NAME_WILD_CARD {
675                schema_name = user_name;
676            }
677
678            if let schema_catalog @ Ok(_) = self.get_schema_by_name(db_name, schema_name) {
679                return schema_catalog;
680            }
681        }
682        Err(CatalogError::not_found(
683            "first valid schema",
684            "no schema has been selected to create in".to_owned(),
685        ))
686    }
687
688    pub fn get_source_by_id<'a>(
689        &self,
690        db_name: &'a str,
691        schema_path: SchemaPath<'a>,
692        source_id: SourceId,
693    ) -> CatalogResult<(&Arc<SourceCatalog>, &'a str)> {
694        schema_path
695            .try_find(|schema_name| -> CatalogResult<_> {
696                Ok(self
697                    .get_schema_by_name(db_name, schema_name)?
698                    .get_source_by_id(source_id))
699            })?
700            .ok_or_else(|| CatalogError::not_found("source", source_id.to_string()))
701    }
702
703    /// Finds a source by ID across all schemas in the given database.
704    pub fn get_source_by_id_with_db(
705        &self,
706        db_name: &str,
707        source_id: SourceId,
708    ) -> CatalogResult<&Arc<SourceCatalog>> {
709        for schema in self.get_database_by_name(db_name)?.iter_schemas() {
710            if let Some(source) = schema.get_source_by_id(source_id) {
711                return Ok(source);
712            }
713        }
714        Err(CatalogError::not_found("source", source_id.to_string()))
715    }
716
717    pub fn get_table_by_name<'a>(
718        &self,
719        db_name: &str,
720        schema_path: SchemaPath<'a>,
721        table_name: &str,
722        bind_creating: bool,
723    ) -> CatalogResult<(&Arc<TableCatalog>, &'a str)> {
724        schema_path
725            .try_find(|schema_name| -> CatalogResult<_> {
726                Ok(self
727                    .get_schema_by_name(db_name, schema_name)?
728                    .get_table_by_name(table_name, bind_creating))
729            })?
730            .ok_or_else(|| CatalogError::not_found("table", table_name))
731    }
732
733    /// Used to get `TableCatalog` for Materialized Views, Tables and Indexes.
734    /// Retrieves all tables, created or creating.
735    pub fn get_any_table_by_name<'a>(
736        &self,
737        db_name: &str,
738        schema_path: SchemaPath<'a>,
739        table_name: &str,
740    ) -> CatalogResult<(&Arc<TableCatalog>, &'a str)> {
741        self.get_table_by_name(db_name, schema_path, table_name, true)
742    }
743
744    /// Used to get `TableCatalog` for Materialized Views, Tables and Indexes.
745    /// Retrieves only created tables.
746    pub fn get_created_table_by_name<'a>(
747        &self,
748        db_name: &str,
749        schema_path: SchemaPath<'a>,
750        table_name: &str,
751    ) -> CatalogResult<(&Arc<TableCatalog>, &'a str)> {
752        self.get_table_by_name(db_name, schema_path, table_name, false)
753    }
754
755    pub fn get_any_table_by_id(&self, table_id: TableId) -> CatalogResult<&Arc<TableCatalog>> {
756        self.table_by_id
757            .get(&table_id)
758            .ok_or_else(|| CatalogError::not_found("table id", table_id.to_string()))
759    }
760
761    /// This function is similar to `get_table_by_id` expect that a table must be in a given database.
762    pub fn get_created_table_by_id_with_db(
763        &self,
764        db_name: &str,
765        table_id: u32,
766    ) -> CatalogResult<&Arc<TableCatalog>> {
767        let table_id = TableId::from(table_id);
768        for schema in self.get_database_by_name(db_name)?.iter_schemas() {
769            if let Some(table) = schema.get_created_table_by_id(table_id) {
770                return Ok(table);
771            }
772        }
773        Err(CatalogError::not_found("table id", table_id.to_string()))
774    }
775
776    pub fn iter_tables(&self) -> impl Iterator<Item = &Arc<TableCatalog>> {
777        self.table_by_id.values()
778    }
779
780    pub fn iter_backfilling_internal_tables(&self) -> impl Iterator<Item = &Arc<TableCatalog>> {
781        self.table_by_id
782            .values()
783            .filter(|t| t.is_internal_table() && !t.is_created())
784    }
785
786    // Used by test_utils only.
787    pub fn alter_table_name_by_id(&mut self, table_id: TableId, table_name: &str) {
788        let mut found = false;
789        for database in self.database_by_name.values() {
790            if !found {
791                for schema in database.iter_schemas() {
792                    if schema.iter_user_table().any(|t| t.id() == table_id) {
793                        found = true;
794                        break;
795                    }
796                }
797            }
798        }
799
800        if found {
801            let mut table = self.get_any_table_by_id(table_id).unwrap().to_prost();
802            table.name = table_name.to_owned();
803            self.update_table(&table);
804        }
805    }
806
807    #[cfg(test)]
808    pub fn insert_table_id_mapping(&mut self, table_id: TableId, fragment_id: super::FragmentId) {
809        self.table_by_id.insert(
810            table_id,
811            Arc::new(TableCatalog {
812                fragment_id,
813                ..Default::default()
814            }),
815        );
816    }
817
818    pub fn get_sys_table_by_name(
819        &self,
820        db_name: &str,
821        schema_name: &str,
822        table_name: &str,
823    ) -> CatalogResult<&Arc<SystemTableCatalog>> {
824        self.get_schema_by_name(db_name, schema_name)?
825            .get_system_table_by_name(table_name)
826            .ok_or_else(|| CatalogError::not_found("table", table_name))
827    }
828
829    pub fn get_source_by_name<'a>(
830        &self,
831        db_name: &str,
832        schema_path: SchemaPath<'a>,
833        source_name: &str,
834    ) -> CatalogResult<(&Arc<SourceCatalog>, &'a str)> {
835        schema_path
836            .try_find(|schema_name| -> CatalogResult<_> {
837                Ok(self
838                    .get_schema_by_name(db_name, schema_name)?
839                    .get_source_by_name(source_name))
840            })?
841            .ok_or_else(|| CatalogError::not_found("source", source_name))
842    }
843
844    pub fn get_sink_by_name<'a>(
845        &self,
846        db_name: &str,
847        schema_path: SchemaPath<'a>,
848        sink_name: &str,
849        bind_creating: bool,
850    ) -> CatalogResult<(&Arc<SinkCatalog>, &'a str)> {
851        schema_path
852            .try_find(|schema_name| -> CatalogResult<_> {
853                Ok(self
854                    .get_schema_by_name(db_name, schema_name)?
855                    .get_sink_by_name(sink_name, bind_creating))
856            })?
857            .ok_or_else(|| CatalogError::not_found("sink", sink_name))
858    }
859
860    pub fn get_any_sink_by_name<'a>(
861        &self,
862        db_name: &str,
863        schema_path: SchemaPath<'a>,
864        sink_name: &str,
865    ) -> CatalogResult<(&Arc<SinkCatalog>, &'a str)> {
866        self.get_sink_by_name(db_name, schema_path, sink_name, true)
867    }
868
869    pub fn get_created_sink_by_name<'a>(
870        &self,
871        db_name: &str,
872        schema_path: SchemaPath<'a>,
873        sink_name: &str,
874    ) -> CatalogResult<(&Arc<SinkCatalog>, &'a str)> {
875        self.get_sink_by_name(db_name, schema_path, sink_name, false)
876    }
877
878    pub fn get_subscription_by_name<'a>(
879        &self,
880        db_name: &str,
881        schema_path: SchemaPath<'a>,
882        subscription_name: &str,
883    ) -> CatalogResult<(&Arc<SubscriptionCatalog>, &'a str)> {
884        schema_path
885            .try_find(|schema_name| -> CatalogResult<_> {
886                Ok(self
887                    .get_schema_by_name(db_name, schema_name)?
888                    .get_subscription_by_name(subscription_name))
889            })?
890            .ok_or_else(|| CatalogError::not_found("subscription", subscription_name))
891    }
892
893    pub fn get_index_by_name<'a>(
894        &self,
895        db_name: &str,
896        schema_path: SchemaPath<'a>,
897        index_name: &str,
898    ) -> CatalogResult<(&Arc<IndexCatalog>, &'a str)> {
899        schema_path
900            .try_find(|schema_name| -> CatalogResult<_> {
901                Ok(self
902                    .get_schema_by_name(db_name, schema_name)?
903                    .get_created_index_by_name(index_name))
904            })?
905            .ok_or_else(|| CatalogError::not_found("index", index_name))
906    }
907
908    pub fn get_any_index_by_name<'a>(
909        &self,
910        db_name: &str,
911        schema_path: SchemaPath<'a>,
912        index_name: &str,
913    ) -> CatalogResult<(&Arc<IndexCatalog>, &'a str)> {
914        schema_path
915            .try_find(|schema_name| -> CatalogResult<_> {
916                Ok(self
917                    .get_schema_by_name(db_name, schema_name)?
918                    .get_any_index_by_name(index_name))
919            })?
920            .ok_or_else(|| CatalogError::not_found("index", index_name))
921    }
922
923    pub fn get_index_by_id(
924        &self,
925        db_name: &str,
926        index_id: u32,
927    ) -> CatalogResult<&Arc<IndexCatalog>> {
928        let index_id = IndexId::from(index_id);
929        for schema in self.get_database_by_name(db_name)?.iter_schemas() {
930            if let Some(index) = schema.get_index_by_id(index_id) {
931                return Ok(index);
932            }
933        }
934        Err(CatalogError::not_found("index", index_id.to_string()))
935    }
936
937    pub fn get_view_by_name<'a>(
938        &self,
939        db_name: &str,
940        schema_path: SchemaPath<'a>,
941        view_name: &str,
942    ) -> CatalogResult<(&Arc<ViewCatalog>, &'a str)> {
943        schema_path
944            .try_find(|schema_name| -> CatalogResult<_> {
945                Ok(self
946                    .get_schema_by_name(db_name, schema_name)?
947                    .get_view_by_name(view_name))
948            })?
949            .ok_or_else(|| CatalogError::not_found("view", view_name))
950    }
951
952    pub fn get_view_by_id(&self, db_name: &str, view_id: u32) -> CatalogResult<Arc<ViewCatalog>> {
953        for schema in self.get_database_by_name(db_name)?.iter_schemas() {
954            if let Some(view) = schema.get_view_by_id(ViewId::from(view_id)) {
955                return Ok(view.clone());
956            }
957        }
958        Err(CatalogError::not_found("view", view_id.to_string()))
959    }
960
961    pub fn get_secret_by_name<'a>(
962        &self,
963        db_name: &str,
964        schema_path: SchemaPath<'a>,
965        secret_name: &str,
966    ) -> CatalogResult<(&Arc<SecretCatalog>, &'a str)> {
967        schema_path
968            .try_find(|schema_name| -> CatalogResult<_> {
969                Ok(self
970                    .get_schema_by_name(db_name, schema_name)?
971                    .get_secret_by_name(secret_name))
972            })?
973            .ok_or_else(|| CatalogError::not_found("secret", secret_name))
974    }
975
976    pub fn get_connection_by_id(
977        &self,
978        db_name: &str,
979        connection_id: ConnectionId,
980    ) -> CatalogResult<&Arc<ConnectionCatalog>> {
981        for schema in self.get_database_by_name(db_name)?.iter_schemas() {
982            if let Some(conn) = schema.get_connection_by_id(connection_id) {
983                return Ok(conn);
984            }
985        }
986        Err(CatalogError::not_found(
987            "connection",
988            connection_id.to_string(),
989        ))
990    }
991
992    pub fn get_connection_by_name<'a>(
993        &self,
994        db_name: &str,
995        schema_path: SchemaPath<'a>,
996        connection_name: &str,
997    ) -> CatalogResult<(&Arc<ConnectionCatalog>, &'a str)> {
998        schema_path
999            .try_find(|schema_name| -> CatalogResult<_> {
1000                Ok(self
1001                    .get_schema_by_name(db_name, schema_name)?
1002                    .get_connection_by_name(connection_name))
1003            })?
1004            .ok_or_else(|| CatalogError::not_found("connection", connection_name))
1005    }
1006
1007    pub fn get_function_by_name_inputs<'a>(
1008        &self,
1009        db_name: &str,
1010        schema_path: SchemaPath<'a>,
1011        function_name: &str,
1012        inputs: &mut [ExprImpl],
1013    ) -> CatalogResult<(&Arc<FunctionCatalog>, &'a str)> {
1014        schema_path
1015            .try_find(|schema_name| -> CatalogResult<_> {
1016                Ok(self
1017                    .get_schema_by_name(db_name, schema_name)?
1018                    .get_function_by_name_inputs(function_name, inputs))
1019            })?
1020            .ok_or_else(|| {
1021                CatalogError::not_found(
1022                    "function",
1023                    format!(
1024                        "{}({})",
1025                        function_name,
1026                        inputs
1027                            .iter()
1028                            .map(|a| a.return_type().to_string())
1029                            .join(", ")
1030                    ),
1031                )
1032            })
1033    }
1034
1035    pub fn get_function_by_name_args<'a>(
1036        &self,
1037        db_name: &str,
1038        schema_path: SchemaPath<'a>,
1039        function_name: &str,
1040        args: &[DataType],
1041    ) -> CatalogResult<(&Arc<FunctionCatalog>, &'a str)> {
1042        schema_path
1043            .try_find(|schema_name| -> CatalogResult<_> {
1044                Ok(self
1045                    .get_schema_by_name(db_name, schema_name)?
1046                    .get_function_by_name_args(function_name, args))
1047            })?
1048            .ok_or_else(|| {
1049                CatalogError::not_found(
1050                    "function",
1051                    format!(
1052                        "{}({})",
1053                        function_name,
1054                        args.iter().map(|a| a.to_string()).join(", ")
1055                    ),
1056                )
1057            })
1058    }
1059
1060    /// Gets all functions with the given name.
1061    pub fn get_functions_by_name<'a>(
1062        &self,
1063        db_name: &str,
1064        schema_path: SchemaPath<'a>,
1065        function_name: &str,
1066    ) -> CatalogResult<(Vec<&Arc<FunctionCatalog>>, &'a str)> {
1067        schema_path
1068            .try_find(|schema_name| -> CatalogResult<_> {
1069                Ok(self
1070                    .get_schema_by_name(db_name, schema_name)?
1071                    .get_functions_by_name(function_name))
1072            })?
1073            .ok_or_else(|| CatalogError::not_found("function", function_name))
1074    }
1075
1076    /// Check if the name duplicates with existing table, materialized view or source.
1077    pub fn check_relation_name_duplicated(
1078        &self,
1079        db_name: &str,
1080        schema_name: &str,
1081        relation_name: &str,
1082    ) -> CatalogResult<()> {
1083        let schema = self.get_schema_by_name(db_name, schema_name)?;
1084
1085        if let Some(table) = schema.get_any_table_by_name(relation_name) {
1086            let is_creating = table.stream_job_status == StreamJobStatus::Creating;
1087            if table.is_index() {
1088                Err(CatalogError::duplicated_under_creation(
1089                    "index",
1090                    relation_name.to_owned(),
1091                    is_creating,
1092                ))
1093            } else if table.is_mview() {
1094                Err(CatalogError::duplicated_under_creation(
1095                    "materialized view",
1096                    relation_name.to_owned(),
1097                    is_creating,
1098                ))
1099            } else {
1100                Err(CatalogError::duplicated_under_creation(
1101                    "table",
1102                    relation_name.to_owned(),
1103                    is_creating,
1104                ))
1105            }
1106        } else if schema.get_source_by_name(relation_name).is_some() {
1107            Err(CatalogError::duplicated("source", relation_name))
1108        } else if let Some(sink) = schema.get_any_sink_by_name(relation_name) {
1109            Err(CatalogError::duplicated_under_creation(
1110                "sink",
1111                relation_name.to_owned(),
1112                !sink.is_created(),
1113            ))
1114        } else if schema.get_view_by_name(relation_name).is_some() {
1115            Err(CatalogError::duplicated("view", relation_name))
1116        } else if let Some(subscription) = schema.get_subscription_by_name(relation_name) {
1117            let is_not_created = subscription.subscription_state != SubscriptionState::Created;
1118            Err(CatalogError::duplicated_under_creation(
1119                "subscription",
1120                relation_name.to_owned(),
1121                is_not_created,
1122            ))
1123        } else {
1124            Ok(())
1125        }
1126    }
1127
1128    pub fn check_function_name_duplicated(
1129        &self,
1130        db_name: &str,
1131        schema_name: &str,
1132        function_name: &str,
1133        arg_types: &[DataType],
1134    ) -> CatalogResult<()> {
1135        let schema = self.get_schema_by_name(db_name, schema_name)?;
1136
1137        if schema
1138            .get_function_by_name_args(function_name, arg_types)
1139            .is_some()
1140        {
1141            let name = format!(
1142                "{function_name}({})",
1143                arg_types.iter().map(|t| t.to_string()).join(",")
1144            );
1145            Err(CatalogError::duplicated("function", name))
1146        } else {
1147            Ok(())
1148        }
1149    }
1150
1151    /// Check if the name duplicates with existing connection.
1152    pub fn check_connection_name_duplicated(
1153        &self,
1154        db_name: &str,
1155        schema_name: &str,
1156        connection_name: &str,
1157    ) -> CatalogResult<()> {
1158        let schema = self.get_schema_by_name(db_name, schema_name)?;
1159
1160        if schema.get_connection_by_name(connection_name).is_some() {
1161            Err(CatalogError::duplicated("connection", connection_name))
1162        } else {
1163            Ok(())
1164        }
1165    }
1166
1167    pub fn check_secret_name_duplicated(
1168        &self,
1169        db_name: &str,
1170        schema_name: &str,
1171        secret_name: &str,
1172    ) -> CatalogResult<()> {
1173        let schema = self.get_schema_by_name(db_name, schema_name)?;
1174
1175        if schema.get_secret_by_name(secret_name).is_some() {
1176            Err(CatalogError::duplicated("secret", secret_name))
1177        } else {
1178            Ok(())
1179        }
1180    }
1181
1182    pub fn table_stats(&self) -> &HummockVersionStats {
1183        &self.table_stats
1184    }
1185
1186    pub fn set_table_stats(&mut self, table_stats: HummockVersionStats) {
1187        self.table_stats = table_stats;
1188    }
1189
1190    pub fn set_object_dependencies(&mut self, dependencies: Vec<PbObjectDependency>) {
1191        self.object_dependencies.clear();
1192        self.insert_object_dependencies(dependencies);
1193    }
1194
1195    pub fn insert_object_dependencies(&mut self, dependencies: Vec<PbObjectDependency>) {
1196        let mut grouped: HashMap<ObjectId, Vec<ObjectDependency>> = HashMap::new();
1197        for dependency in dependencies {
1198            let dependency = ObjectDependency::from(dependency);
1199            grouped
1200                .entry(dependency.object_id)
1201                .or_default()
1202                .push(dependency);
1203        }
1204        for (object_id, deps) in grouped {
1205            self.object_dependencies.insert(object_id, deps);
1206        }
1207    }
1208
1209    pub fn iter_object_dependencies(&self) -> impl Iterator<Item = &ObjectDependency> {
1210        self.object_dependencies
1211            .values()
1212            .flat_map(|deps| deps.iter())
1213    }
1214
1215    fn remove_object_dependencies_for_object(&mut self, object_id: ObjectId) {
1216        let mut object_ids = HashSet::new();
1217        object_ids.insert(object_id);
1218        self.remove_object_dependencies_for_objects(&object_ids);
1219    }
1220
1221    fn remove_object_dependencies_for_objects(&mut self, object_ids: &HashSet<ObjectId>) {
1222        if object_ids.is_empty() {
1223            return;
1224        }
1225        self.object_dependencies.retain(|object_id, deps| {
1226            if object_ids.contains(object_id) {
1227                return false;
1228            }
1229            deps.retain(|dep| !object_ids.contains(&dep.referenced_object_id));
1230            !deps.is_empty()
1231        });
1232    }
1233
1234    pub fn get_all_indexes_related_to_object(
1235        &self,
1236        db_id: DatabaseId,
1237        schema_id: SchemaId,
1238        mv_id: TableId,
1239    ) -> Vec<Arc<IndexCatalog>> {
1240        self.get_database_by_id(db_id)
1241            .unwrap()
1242            .get_schema_by_id(schema_id)
1243            .unwrap()
1244            .get_any_indexes_by_table_id(mv_id)
1245    }
1246
1247    pub fn get_id_by_class_name(
1248        &self,
1249        db_name: &str,
1250        schema_path: SchemaPath<'_>,
1251        class_name: &str,
1252    ) -> CatalogResult<ObjectId> {
1253        schema_path
1254            .try_find(|schema_name| -> CatalogResult<_> {
1255                let schema = self.get_schema_by_name(db_name, schema_name)?;
1256
1257                if let Some(item) = schema.get_system_table_by_name(class_name) {
1258                    Ok(Some(item.id().as_object_id()))
1259                } else if let Some(item) = schema.get_any_table_by_name(class_name) {
1260                    Ok(Some(item.id().as_object_id()))
1261                } else if let Some(item) = schema.get_any_index_by_name(class_name) {
1262                    Ok(Some(item.id.as_object_id()))
1263                } else if let Some(item) = schema.get_source_by_name(class_name) {
1264                    Ok(Some(item.id.as_object_id()))
1265                } else if let Some(item) = schema.get_view_by_name(class_name) {
1266                    Ok(Some(item.id.as_object_id()))
1267                } else if let Some(item) = schema.get_any_sink_by_name(class_name) {
1268                    Ok(Some(item.id.as_object_id()))
1269                } else {
1270                    Ok(None)
1271                }
1272            })?
1273            .map(|(id, _)| id)
1274            .ok_or_else(|| CatalogError::not_found("class", class_name))
1275    }
1276}
1277
1278#[cfg(test)]
1279mod tests {
1280    use risingwave_pb::catalog::{
1281        PbCreateType, PbDatabase, PbSchema, PbSink, PbSinkType, PbStreamJobStatus,
1282    };
1283
1284    use super::Catalog;
1285
1286    fn test_sink(id: u32, name: &str, database_id: u32, schema_id: u32) -> PbSink {
1287        PbSink {
1288            id: id.into(),
1289            name: name.to_owned(),
1290            database_id: database_id.into(),
1291            schema_id: schema_id.into(),
1292            sink_type: PbSinkType::AppendOnly.into(),
1293            stream_job_status: PbStreamJobStatus::Creating.into(),
1294            create_type: PbCreateType::Background.into(),
1295            ..Default::default()
1296        }
1297    }
1298
1299    #[test]
1300    fn test_update_sink_set_schema_drops_old_catalog() {
1301        let database_id = 1;
1302        let old_schema_id = 2;
1303        let new_schema_id = 3;
1304        let sink_id = 4;
1305        let mut catalog = Catalog::default();
1306        catalog.create_database(&PbDatabase {
1307            id: database_id.into(),
1308            name: "dev".to_owned(),
1309            ..Default::default()
1310        });
1311        catalog.create_schema(&PbSchema {
1312            id: old_schema_id.into(),
1313            database_id: database_id.into(),
1314            name: "public".to_owned(),
1315            ..Default::default()
1316        });
1317        catalog.create_schema(&PbSchema {
1318            id: new_schema_id.into(),
1319            database_id: database_id.into(),
1320            name: "archive".to_owned(),
1321            ..Default::default()
1322        });
1323        catalog.create_sink(&test_sink(
1324            sink_id,
1325            "replace_kafka_sink",
1326            database_id,
1327            old_schema_id,
1328        ));
1329
1330        catalog.update_sink(&test_sink(
1331            sink_id,
1332            "replace_kafka_sink",
1333            database_id,
1334            new_schema_id,
1335        ));
1336
1337        let old_schema = catalog
1338            .get_schema_by_id(database_id.into(), old_schema_id.into())
1339            .unwrap();
1340        assert!(old_schema.get_sink_by_id(sink_id.into()).is_none());
1341        let new_schema = catalog
1342            .get_schema_by_id(database_id.into(), new_schema_id.into())
1343            .unwrap();
1344        assert!(new_schema.get_sink_by_id(sink_id.into()).is_some());
1345    }
1346}