1use std::fmt;
4
5use rudb_common::{Error, Field, Result};
6
7use crate::name::{QualifiedName, same_name};
8use crate::table::Table;
9use crate::view::View;
10
11pub const DEFAULT_CATALOG: &str = "memory";
13pub const DEFAULT_SCHEMA: &str = "main";
15
16#[derive(Debug, Clone)]
18pub struct Database {
19 name: String,
20 schemas: Vec<Schema>,
21}
22
23impl Database {
24 #[must_use]
26 pub fn name(&self) -> &str {
27 &self.name
28 }
29
30 #[must_use]
32 pub fn schemas(&self) -> &[Schema] {
33 &self.schemas
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Entry {
45 Table,
47 View,
49}
50
51impl fmt::Display for Entry {
52 fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
53 out.write_str(match self {
54 Self::Table => "Table",
55 Self::View => "View",
56 })
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct Schema {
63 name: String,
64 tables: Vec<Table>,
65 views: Vec<View>,
66}
67
68impl Schema {
69 fn empty(name: &str) -> Self {
71 Self { name: name.to_string(), tables: Vec::new(), views: Vec::new() }
72 }
73
74 #[must_use]
76 pub fn name(&self) -> &str {
77 &self.name
78 }
79
80 #[must_use]
82 pub fn tables(&self) -> &[Table] {
83 &self.tables
84 }
85
86 #[must_use]
88 pub fn views(&self) -> &[View] {
89 &self.views
90 }
91
92 fn kind(&self, name: &str) -> Option<Entry> {
94 if self.tables.iter().any(|held| same_name(&held.name().table, name)) {
95 return Some(Entry::Table);
96 }
97 if self.views.iter().any(|held| same_name(&held.name().table, name)) {
98 return Some(Entry::View);
99 }
100 None
101 }
102}
103
104#[derive(Debug, Clone)]
114pub struct Catalog {
115 databases: Vec<Database>,
116 default_catalog: String,
117 default_schema: String,
118}
119
120impl Default for Catalog {
121 fn default() -> Self {
122 Self::new()
123 }
124}
125
126impl Catalog {
127 #[must_use]
130 pub fn new() -> Self {
131 Self {
132 databases: vec![Database {
133 name: DEFAULT_CATALOG.to_string(),
134 schemas: vec![Schema::empty(DEFAULT_SCHEMA)],
135 }],
136 default_catalog: DEFAULT_CATALOG.to_string(),
137 default_schema: DEFAULT_SCHEMA.to_string(),
138 }
139 }
140
141 #[must_use]
143 pub fn default_catalog(&self) -> &str {
144 &self.default_catalog
145 }
146
147 #[must_use]
149 pub fn default_schema(&self) -> &str {
150 &self.default_schema
151 }
152
153 #[must_use]
155 pub fn databases(&self) -> &[Database] {
156 &self.databases
157 }
158
159 pub fn attach(&mut self, name: &str) -> Result<()> {
165 if self.databases.iter().any(|held| same_name(&held.name, name)) {
166 return Err(Error::catalog(format!("Database with name \"{name}\" already exists!")));
167 }
168 self.databases.push(Database {
169 name: name.to_string(),
170 schemas: vec![Schema::empty(DEFAULT_SCHEMA)],
171 });
172 Ok(())
173 }
174
175 pub fn create_schema(&mut self, catalog: &str, name: &str) -> Result<()> {
181 let database = self.database_mut(catalog)?;
182 if database.schemas.iter().any(|held| same_name(&held.name, name)) {
183 return Err(Error::catalog(format!("Schema with name \"{name}\" already exists!")));
184 }
185 database.schemas.push(Schema::empty(name));
186 Ok(())
187 }
188
189 pub fn create_table(&mut self, name: QualifiedName, columns: Vec<Field>) -> Result<()> {
196 let table = Table::new(name.clone(), columns)?;
197 let schema = self.schema_mut(&name.catalog, &name.schema)?;
198 if let Some(found) = schema.kind(&name.table) {
199 return Err(taken(found, &name.table));
200 }
201 schema.tables.push(table);
202 Ok(())
203 }
204
205 pub fn create_view(&mut self, view: View) -> Result<()> {
215 let name = view.name().clone();
216 let schema = self.schema_mut(&name.catalog, &name.schema)?;
217 if let Some(found) = schema.kind(&name.table) {
218 return Err(taken(found, &name.table));
219 }
220 schema.views.push(view);
221 Ok(())
222 }
223
224 pub fn drop_table(&mut self, name: &QualifiedName) -> Result<()> {
231 self.drop_entry(name, Entry::Table)
232 }
233
234 pub fn drop_view(&mut self, name: &QualifiedName) -> Result<()> {
240 self.drop_entry(name, Entry::View)
241 }
242
243 fn drop_entry(&mut self, name: &QualifiedName, wanted: Entry) -> Result<()> {
245 let schema = self.schema_mut(&name.catalog, &name.schema)?;
246 match schema.kind(&name.table) {
247 None => Err(missing(wanted, &name.table)),
250 Some(found) if found != wanted => Err(Error::catalog(format!(
251 "Existing object \"{}\" is of type {found}, trying to drop type {wanted}",
252 name.table
253 ))),
254 Some(Entry::Table) => {
255 schema.tables.retain(|held| !same_name(&held.name().table, &name.table));
256 Ok(())
257 }
258 Some(Entry::View) => {
259 schema.views.retain(|held| !same_name(&held.name().table, &name.table));
260 Ok(())
261 }
262 }
263 }
264
265 pub fn entry(&self, name: &QualifiedName) -> Result<Entry> {
271 self.schema(&name.catalog, &name.schema)?
272 .kind(&name.table)
273 .ok_or_else(|| missing_table(&name.table))
274 }
275
276 pub fn view(&self, name: &QualifiedName) -> Result<&View> {
282 let schema = self.schema(&name.catalog, &name.schema)?;
283 schema
284 .views
285 .iter()
286 .find(|held| same_name(&held.name().table, &name.table))
287 .ok_or_else(|| missing_table(&name.table))
288 }
289
290 pub fn table(&self, name: &QualifiedName) -> Result<&Table> {
296 let schema = self.schema(&name.catalog, &name.schema)?;
297 schema
298 .tables
299 .iter()
300 .find(|held| same_name(&held.name().table, &name.table))
301 .ok_or_else(|| missing_table(&name.table))
302 }
303
304 pub fn table_mut(&mut self, name: &QualifiedName) -> Result<&mut Table> {
310 let table = name.table.clone();
311 let schema = self.schema_mut(&name.catalog, &name.schema)?;
312 schema
313 .tables
314 .iter_mut()
315 .find(|held| same_name(&held.name().table, &table))
316 .ok_or_else(|| missing_table(&table))
317 }
318
319 pub fn resolve(&self, parts: &[&str]) -> Result<QualifiedName> {
334 self.resolve_as(parts, Entry::Table)
335 }
336
337 pub fn resolve_as(&self, parts: &[&str], wanted: Entry) -> Result<QualifiedName> {
349 let candidates = self.candidates(parts)?;
350 let mut first_error = None;
351 for candidate in &candidates {
352 let held = match self.schema(&candidate.catalog, &candidate.schema) {
353 Ok(schema) => schema.kind(&candidate.table),
354 Err(error) => {
357 first_error = first_error.or(Some(error));
358 continue;
359 }
360 };
361 match held {
362 Some(Entry::Table) => return Ok(self.table(candidate)?.name().clone()),
363 Some(Entry::View) => return Ok(self.view(candidate)?.name().clone()),
364 None => {
365 first_error = first_error.or_else(|| Some(missing(wanted, &candidate.table)));
366 }
367 }
368 }
369 Err(first_error.unwrap_or_else(|| missing(wanted, &parts.join("."))))
370 }
371
372 pub fn resolve_for_create(&self, parts: &[&str]) -> Result<QualifiedName> {
378 let candidates = self.candidates(parts)?;
379 let mut first_error = None;
380 for candidate in &candidates {
381 match self.schema(&candidate.catalog, &candidate.schema) {
382 Ok(_) => return Ok(candidate.clone()),
383 Err(error) => first_error = first_error.or(Some(error)),
384 }
385 }
386 Err(first_error.unwrap_or_else(|| {
387 Error::catalog(format!("Schema with name {} does not exist!", parts.join(".")))
388 }))
389 }
390
391 pub fn tables(&self) -> impl Iterator<Item = &Table> {
393 self.databases
394 .iter()
395 .flat_map(|database| database.schemas.iter())
396 .flat_map(|schema| schema.tables.iter())
397 }
398
399 fn candidates(&self, parts: &[&str]) -> Result<Vec<QualifiedName>> {
401 match parts {
402 [table] => {
403 Ok(vec![QualifiedName::new(&self.default_catalog, &self.default_schema, *table)])
404 }
405 [first, table] => Ok(vec![
406 QualifiedName::new(&self.default_catalog, *first, *table),
407 QualifiedName::new(*first, &self.default_schema, *table),
408 ]),
409 [catalog, schema, table] => Ok(vec![QualifiedName::new(*catalog, *schema, *table)]),
410 _ => Err(Error::catalog(format!(
411 "a name of {} parts, and a table name has one, two or three",
412 parts.len()
413 ))),
414 }
415 }
416
417 fn database(&self, catalog: &str) -> Result<&Database> {
418 self.databases
419 .iter()
420 .find(|held| same_name(&held.name, catalog))
421 .ok_or_else(|| Error::catalog(format!("Catalog with name {catalog} does not exist!")))
422 }
423
424 fn database_mut(&mut self, catalog: &str) -> Result<&mut Database> {
425 self.databases
426 .iter_mut()
427 .find(|held| same_name(&held.name, catalog))
428 .ok_or_else(|| Error::catalog(format!("Catalog with name {catalog} does not exist!")))
429 }
430
431 fn schema(&self, catalog: &str, schema: &str) -> Result<&Schema> {
432 self.database(catalog)?
433 .schemas
434 .iter()
435 .find(|held| same_name(&held.name, schema))
436 .ok_or_else(|| Error::catalog(format!("Schema with name {schema} does not exist!")))
437 }
438
439 fn schema_mut(&mut self, catalog: &str, schema: &str) -> Result<&mut Schema> {
440 self.database_mut(catalog)?
441 .schemas
442 .iter_mut()
443 .find(|held| same_name(&held.name, schema))
444 .ok_or_else(|| Error::catalog(format!("Schema with name {schema} does not exist!")))
445 }
446}
447
448fn missing_table(name: &str) -> Error {
449 Error::catalog(format!("Table with name {name} does not exist!"))
450}
451
452fn missing(wanted: Entry, name: &str) -> Error {
458 Error::catalog(format!("{wanted} with name {name} does not exist!"))
459}
460
461fn taken(found: Entry, name: &str) -> Error {
472 Error::catalog(format!("{found} with name \"{name}\" already exists!"))
473}
474
475#[cfg(test)]
476mod tests {
477 use rudb_common::LogicalType;
478
479 use super::*;
480 use crate::view::View;
481
482 fn with_hits() -> Catalog {
483 let mut catalog = Catalog::new();
484 catalog
485 .create_table(
486 QualifiedName::new("memory", "main", "hits"),
487 vec![
488 Field::new("UserID", LogicalType::BigInt),
489 Field::new("SearchPhrase", LogicalType::Varchar),
490 ],
491 )
492 .expect("a table in the default schema");
493 catalog
494 }
495
496 #[test]
497 fn a_fresh_catalog_has_the_in_memory_database_in_it() {
498 let catalog = Catalog::new();
499 assert_eq!(catalog.default_catalog(), "memory");
500 assert_eq!(catalog.default_schema(), "main");
501 assert_eq!(catalog.databases().len(), 1);
502 assert_eq!(catalog.tables().count(), 0);
503 }
504
505 #[test]
506 fn an_unqualified_name_resolves_in_the_default_schema() {
507 let catalog = with_hits();
508 let name = catalog.resolve(&["HITS"]).expect("the table exists whatever the case");
509 assert_eq!(name.to_string(), "memory.main.hits");
510 }
511
512 #[test]
513 fn a_three_part_name_resolves_to_itself() {
514 let catalog = with_hits();
515 let name = catalog.resolve(&["memory", "main", "hits"]).expect("the full name");
516 assert_eq!(name.to_string(), "memory.main.hits");
517 }
518
519 #[test]
523 fn two_parts_are_a_schema_first_and_a_catalog_second() {
524 let mut catalog = with_hits();
525 catalog.create_schema("memory", "reporting").expect("a second schema");
526 catalog
527 .create_table(
528 QualifiedName::new("memory", "reporting", "hits"),
529 vec![Field::new("n", LogicalType::Integer)],
530 )
531 .expect("a table in it");
532
533 let by_schema = catalog.resolve(&["reporting", "hits"]).expect("the reporting one");
534 assert_eq!(by_schema.to_string(), "memory.reporting.hits");
535
536 let by_catalog = catalog.resolve(&["memory", "hits"]).expect("the default schema one");
537 assert_eq!(by_catalog.to_string(), "memory.main.hits");
538 }
539
540 #[test]
541 fn a_table_that_is_not_there_says_so_the_way_duckdb_does() {
542 let catalog = with_hits();
543 let error = catalog.resolve(&["nope"]).expect_err("there is no table called nope");
544 assert_eq!(error.to_string(), "Catalog Error: Table with name nope does not exist!");
545 }
546
547 #[test]
548 fn a_schema_that_is_not_there_says_which_schema() {
549 let catalog = with_hits();
550 let error =
551 catalog.resolve(&["memory", "nope", "hits"]).expect_err("there is no schema nope");
552 assert_eq!(error.to_string(), "Catalog Error: Schema with name nope does not exist!");
553 }
554
555 #[test]
556 fn creating_the_same_table_twice_is_an_error() {
557 let mut catalog = with_hits();
558 let error = catalog
559 .create_table(
560 QualifiedName::new("memory", "main", "HITS"),
561 vec![Field::new("n", LogicalType::Integer)],
562 )
563 .expect_err("hits is already there");
564 assert_eq!(error.to_string(), "Catalog Error: Table with name \"HITS\" already exists!");
565 }
566
567 #[test]
568 fn a_dropped_table_is_gone() {
569 let mut catalog = with_hits();
570 let name = catalog.resolve(&["hits"]).expect("it is there");
571 catalog.drop_table(&name).expect("dropping it works");
572 assert!(catalog.resolve(&["hits"]).is_err(), "it is not there any more");
573 assert!(catalog.drop_table(&name).is_err(), "dropping it twice does not");
574 }
575
576 #[test]
577 fn a_name_for_a_create_does_not_have_to_exist_yet() {
578 let catalog = Catalog::new();
579 let name = catalog.resolve_for_create(&["new_table"]).expect("the default schema is there");
580 assert_eq!(name.to_string(), "memory.main.new_table");
581 assert!(
582 catalog.resolve_for_create(&["nope", "new_table"]).is_err(),
583 "a schema that is not there is still an error"
584 );
585 }
586
587 #[test]
588 fn a_name_of_four_parts_is_not_a_table_name() {
589 let catalog = Catalog::new();
590 let error = catalog.resolve(&["a", "b", "c", "d"]).expect_err("four parts");
591 assert!(error.message().contains("4 parts"), "{error}");
592 }
593
594 fn with_view() -> Catalog {
595 let mut catalog = with_hits();
596 catalog
597 .create_view(View::new(
598 QualifiedName::new("memory", "main", "recent"),
599 "SELECT * FROM hits".to_string(),
600 Vec::new(),
601 ))
602 .expect("a view in the default schema");
603 catalog
604 }
605
606 #[test]
607 fn a_view_resolves_the_way_a_table_does() {
608 let catalog = with_view();
609 let name = catalog.resolve(&["RECENT"]).expect("the view, whatever the case");
610 assert_eq!(name.to_string(), "memory.main.recent");
611 assert_eq!(catalog.entry(&name).expect("it is there"), Entry::View);
612 assert_eq!(catalog.view(&name).expect("the body").sql(), "SELECT * FROM hits");
613 }
614
615 #[test]
616 fn the_two_share_one_namespace_and_the_message_names_what_was_being_made() {
617 let mut catalog = with_view();
618 let error = catalog
619 .create_table(
620 QualifiedName::new("memory", "main", "recent"),
621 vec![Field::new("n", LogicalType::Integer)],
622 )
623 .expect_err("recent is a view");
624 assert_eq!(error.to_string(), "Catalog Error: View with name \"recent\" already exists!");
625
626 let error = catalog
627 .create_view(View::new(
628 QualifiedName::new("memory", "main", "HITS"),
629 "SELECT 1".to_string(),
630 Vec::new(),
631 ))
632 .expect_err("hits is a table");
633 assert_eq!(error.to_string(), "Catalog Error: Table with name \"HITS\" already exists!");
634 }
635
636 #[test]
637 fn dropping_one_as_the_other_names_both_types() {
638 let mut catalog = with_view();
639 let view = catalog.resolve(&["recent"]).expect("the view");
640 let error = catalog.drop_table(&view).expect_err("it is a view");
641 assert_eq!(
642 error.to_string(),
643 "Catalog Error: Existing object \"recent\" is of type View, trying to drop type Table"
644 );
645 let table = catalog.resolve(&["hits"]).expect("the table");
646 let error = catalog.drop_view(&table).expect_err("it is a table");
647 assert_eq!(
648 error.to_string(),
649 "Catalog Error: Existing object \"hits\" is of type Table, trying to drop type View"
650 );
651 catalog.drop_view(&view).expect("dropping it as what it is");
652 assert!(catalog.resolve(&["recent"]).is_err(), "it is gone");
653 }
654
655 #[test]
656 fn attaching_gives_a_second_database_with_its_own_main() {
657 let mut catalog = with_hits();
658 catalog.attach("other").expect("a second database");
659 catalog
660 .create_table(
661 QualifiedName::new("other", "main", "hits"),
662 vec![Field::new("n", LogicalType::Integer)],
663 )
664 .expect("a table of the same name in it");
665 assert_eq!(catalog.tables().count(), 2);
666 let name = catalog.resolve(&["other", "main", "hits"]).expect("the other one");
667 assert_eq!(name.catalog, "other");
668 assert!(catalog.attach("OTHER").is_err(), "attaching it twice does not work");
669 }
670}