Skip to main content

narwhal_core/
schema.rs

1use serde::{Deserialize, Serialize};
2
3/// All visible schemas paired with the tables they own.
4///
5/// Return type of [`crate::Connection::list_all_tables`]. Lives at
6/// the crate root so the dyn-safe sibling signatures don't trip
7/// `clippy::type_complexity` on the nested `Vec<(Schema,
8/// Vec<Table>)>`.
9pub type SchemaCatalog = Vec<(Schema, Vec<Table>)>;
10
11use crate::value::Value;
12
13/// Logical schema or namespace inside a database.
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct Schema {
16    pub name: String,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[non_exhaustive]
21pub enum TableKind {
22    Table,
23    View,
24    MaterializedView,
25    SystemTable,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct Table {
30    pub schema: String,
31    pub name: String,
32    pub kind: TableKind,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct Column {
37    pub name: String,
38    /// Native type name as reported by the engine (e.g. `int4`, `varchar(255)`).
39    pub data_type: String,
40    pub nullable: bool,
41    pub primary_key: bool,
42    pub default: Option<String>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct TableSchema {
47    pub table: Table,
48    pub columns: Vec<Column>,
49    #[serde(default)]
50    pub indexes: Vec<Index>,
51    #[serde(default)]
52    pub foreign_keys: Vec<ForeignKey>,
53    #[serde(default)]
54    pub unique_constraints: Vec<UniqueConstraint>,
55}
56
57/// Index defined on a table.
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct Index {
60    pub name: String,
61    pub columns: Vec<String>,
62    pub unique: bool,
63    /// `true` when the index is the implicit one created for the primary
64    /// key. Useful when generating DDL, where the index is implied by the
65    /// `PRIMARY KEY` declaration on the column instead.
66    pub primary: bool,
67}
68
69/// Single foreign-key constraint.
70///
71/// Composite foreign keys are represented by parallel entries in
72/// [`Self::columns`] and [`Self::referenced_columns`].
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub struct ForeignKey {
75    pub name: String,
76    pub columns: Vec<String>,
77    pub referenced_schema: Option<String>,
78    pub referenced_table: String,
79    pub referenced_columns: Vec<String>,
80    pub on_update: Option<ReferentialAction>,
81    pub on_delete: Option<ReferentialAction>,
82}
83
84/// Referential action declared on a foreign key.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[non_exhaustive]
87pub enum ReferentialAction {
88    NoAction,
89    Restrict,
90    Cascade,
91    SetNull,
92    SetDefault,
93}
94
95impl ReferentialAction {
96    pub const fn as_sql(self) -> &'static str {
97        match self {
98            Self::NoAction => "NO ACTION",
99            Self::Restrict => "RESTRICT",
100            Self::Cascade => "CASCADE",
101            Self::SetNull => "SET NULL",
102            Self::SetDefault => "SET DEFAULT",
103        }
104    }
105
106    pub fn from_engine_token(token: &str) -> Option<Self> {
107        match token.trim().to_ascii_uppercase().as_str() {
108            "NO ACTION" => Some(Self::NoAction),
109            "RESTRICT" => Some(Self::Restrict),
110            "CASCADE" => Some(Self::Cascade),
111            "SET NULL" => Some(Self::SetNull),
112            "SET DEFAULT" => Some(Self::SetDefault),
113            _ => None,
114        }
115    }
116}
117
118/// Multi-column unique constraint.
119///
120/// Single-column unique constraints are exposed through [`Index::unique`].
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct UniqueConstraint {
123    pub name: String,
124    pub columns: Vec<String>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct Row(pub Vec<Value>);
129
130impl Row {
131    pub fn get(&self, idx: usize) -> Option<&Value> {
132        self.0.get(idx)
133    }
134
135    pub fn len(&self) -> usize {
136        self.0.len()
137    }
138
139    pub fn is_empty(&self) -> bool {
140        self.0.is_empty()
141    }
142}
143
144/// Materialised result of an executed statement.
145///
146/// For non-`SELECT` statements `columns` and `rows` are empty and
147/// `rows_affected` carries the engine-reported count when available.
148#[derive(Debug, Clone, Default, Serialize, Deserialize)]
149pub struct QueryResult {
150    pub columns: Vec<ColumnHeader>,
151    pub rows: Vec<Row>,
152    pub rows_affected: Option<u64>,
153    pub elapsed_ms: u64,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ColumnHeader {
158    pub name: String,
159    pub data_type: String,
160}
161
162impl QueryResult {
163    pub fn empty() -> Self {
164        Self::default()
165    }
166}