mongreldb_core/
external_table.rs1use crate::error::{MongrelError, Result};
2use crate::schema::Schema;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::collections::HashSet;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ExternalTableEntry {
9 pub name: String,
10 pub module: String,
11 #[serde(default)]
12 pub args: Vec<ModuleArg>,
13 pub declared_schema: Schema,
14 #[serde(default)]
15 pub hidden_columns: Vec<String>,
16 #[serde(default)]
17 pub options: BTreeMap<String, String>,
18 #[serde(default)]
19 pub capabilities: ModuleCapabilities,
20 pub created_epoch: u64,
21}
22
23#[derive(Debug, Clone)]
24pub struct ExternalTableDefinition {
25 pub module: String,
26 pub args: Vec<ModuleArg>,
27 pub declared_schema: Schema,
28 pub hidden_columns: Vec<String>,
29 pub options: BTreeMap<String, String>,
30 pub capabilities: ModuleCapabilities,
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34#[serde(rename_all = "snake_case", tag = "kind", content = "value")]
35pub enum ModuleArg {
36 Ident(String),
37 String(String),
38 Number(String),
39}
40
41#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
42pub struct ModuleCapabilities {
43 pub read_only: bool,
44 pub insert_only: bool,
45 pub writable: bool,
46 pub deterministic: bool,
47 pub trigger_safe: bool,
48 pub transaction_safe: bool,
49}
50
51impl ExternalTableEntry {
52 pub fn new(
53 name: impl Into<String>,
54 definition: ExternalTableDefinition,
55 created_epoch: u64,
56 ) -> Result<Self> {
57 let entry = Self {
58 name: name.into(),
59 module: definition.module,
60 args: definition.args,
61 declared_schema: definition.declared_schema,
62 hidden_columns: definition.hidden_columns,
63 options: definition.options,
64 capabilities: definition.capabilities,
65 created_epoch,
66 };
67 entry.validate()?;
68 Ok(entry)
69 }
70
71 pub fn validate(&self) -> Result<()> {
72 validate_name("external table", &self.name)?;
73 validate_name("external table module", &self.module)?;
74 let mut hidden = HashSet::new();
75 for col in &self.hidden_columns {
76 validate_name("external table hidden column", col)?;
77 if !hidden.insert(col) {
78 return Err(MongrelError::InvalidArgument(format!(
79 "duplicate external table hidden column {col:?}"
80 )));
81 }
82 }
83 self.declared_schema.validate_auto_increment()?;
84 Ok(())
85 }
86}
87
88fn validate_name(kind: &str, name: &str) -> Result<()> {
89 if name.is_empty()
90 || !name
91 .chars()
92 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
93 {
94 return Err(MongrelError::InvalidArgument(format!(
95 "{kind} name {name:?} must be non-empty ASCII alphanumeric, '_' or '-'"
96 )));
97 }
98 Ok(())
99}