Skip to main content

gluesql_core/executor/
execute.rs

1use {
2    super::{
3        alter::{
4            CreateTableOptions, alter_table, create_index, create_table, delete_function,
5            drop_table, insert_function,
6        },
7        delete::delete,
8        fetch::fetch,
9        insert::insert,
10        query, select,
11        update::Update,
12        validate::{ColumnValidation, validate_unique},
13    },
14    crate::{
15        ast::{BinaryOperator, DataType, Dictionary, Literal, Variable},
16        data::{Key, Row, SCHEMALESS_DOC_COLUMN, Schema, Value},
17        plan::{
18            DictionarySourcePlan, ExprPlan, FilterInputPlan, FilterPlan, ProjectInputPlan,
19            ProjectPlan, ProjectionPlan, QueryPlan, SelectItemPlan, SourcePlan, StatementPlan,
20            TableAliasPlan,
21        },
22        result::{Error, Result},
23        store::{GStore, GStoreMut},
24    },
25    serde::{Deserialize, Serialize},
26    std::{
27        collections::{BTreeMap, HashMap},
28        env::var,
29        fmt::Debug,
30        rc::Rc,
31    },
32    thiserror::Error as ThisError,
33};
34
35#[derive(ThisError, Serialize, Debug, PartialEq, Eq)]
36pub enum ExecuteError {
37    #[error("table not found: {0}")]
38    TableNotFound(String),
39
40    #[error("expected Map value in _doc column")]
41    ExpectedMapValueInDocColumn,
42}
43
44#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
45pub enum Payload {
46    ShowColumns(Vec<(String, DataType)>),
47    Create,
48    Insert(usize),
49    Select {
50        labels: Vec<String>,
51        rows: Vec<Vec<Value>>,
52    },
53    SelectMap(Vec<BTreeMap<String, Value>>),
54    Delete(usize),
55    Update(usize),
56    DropTable(usize),
57    DropFunction,
58    AlterTable,
59    CreateIndex,
60    DropIndex,
61    StartTransaction,
62    Commit,
63    Rollback,
64    ShowVariable(PayloadVariable),
65}
66
67impl Payload {
68    /// Exports `select` payloads as an [`std::iter::Iterator`].
69    ///
70    /// The items of the Iterator are `HashMap<Column, Value>`, and they are borrowed by default.
71    /// If ownership is required, you need to acquire them directly.
72    ///
73    /// - Some: [`Payload::Select`], [`Payload::SelectMap`]
74    /// - None: otherwise
75    pub fn select(&self) -> Option<impl Iterator<Item = HashMap<&str, &Value>>> {
76        #[derive(iter_enum::Iterator)]
77        enum Iter<I1, I2> {
78            Schema(I1),
79            Schemaless(I2),
80        }
81
82        Some(match self {
83            Payload::Select { labels, rows } => Iter::Schema(rows.iter().map(move |row| {
84                labels
85                    .iter()
86                    .zip(row.iter())
87                    .map(|(label, value)| (label.as_str(), value))
88                    .collect::<HashMap<_, _>>()
89            })),
90            Payload::SelectMap(rows) => Iter::Schemaless(rows.iter().map(|row| {
91                row.iter()
92                    .map(|(k, v)| (k.as_str(), v))
93                    .collect::<HashMap<_, _>>()
94            })),
95            _ => return None,
96        })
97    }
98}
99
100#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
101pub enum PayloadVariable {
102    Tables(Vec<String>),
103    Functions(Vec<String>),
104    Version(String),
105}
106
107pub fn execute<T: GStore + GStoreMut>(
108    storage: &mut T,
109    statement: &StatementPlan,
110) -> Result<Payload> {
111    if matches!(
112        statement,
113        StatementPlan::StartTransaction | StatementPlan::Rollback | StatementPlan::Commit
114    ) {
115        return execute_inner(storage, statement);
116    }
117
118    let autocommit = storage.begin(true)?;
119    let result = execute_inner(storage, statement);
120
121    if !autocommit {
122        return result;
123    }
124
125    match result {
126        Ok(payload) => storage.commit().map(|()| payload),
127        Err(error) => {
128            storage.rollback()?;
129
130            Err(error)
131        }
132    }
133}
134
135fn execute_inner<T: GStore + GStoreMut>(
136    storage: &mut T,
137    statement: &StatementPlan,
138) -> Result<Payload> {
139    match statement {
140        //- Modification
141        //-- Tables
142        StatementPlan::CreateTable {
143            name,
144            columns,
145            if_not_exists,
146            source,
147            engine,
148            foreign_keys,
149            comment,
150        } => {
151            let options = CreateTableOptions {
152                target_table_name: name,
153                column_defs: columns.as_ref().map(Vec::as_slice),
154                if_not_exists: *if_not_exists,
155                source,
156                engine,
157                foreign_keys,
158                comment,
159            };
160
161            create_table(storage, options).map(|()| Payload::Create)
162        }
163        StatementPlan::DropTable {
164            names,
165            if_exists,
166            cascade,
167            ..
168        } => drop_table(storage, names, *if_exists, *cascade).map(Payload::DropTable),
169        StatementPlan::AlterTable { name, operation } => {
170            alter_table(storage, name, operation).map(|()| Payload::AlterTable)
171        }
172        StatementPlan::CreateIndex {
173            name,
174            table_name,
175            column,
176        } => create_index(storage, table_name, name, column).map(|()| Payload::CreateIndex),
177        StatementPlan::DropIndex { name, table_name } => storage
178            .drop_index(table_name, name)
179            .map(|()| Payload::DropIndex),
180        //- Transaction
181        StatementPlan::StartTransaction => storage.begin(false).map(|_| Payload::StartTransaction),
182        StatementPlan::Commit => storage.commit().map(|()| Payload::Commit),
183        StatementPlan::Rollback => storage.rollback().map(|()| Payload::Rollback),
184        //-- Rows
185        StatementPlan::Insert {
186            table_name,
187            columns,
188            source,
189        } => insert(storage, table_name, columns, source).map(Payload::Insert),
190        StatementPlan::Update {
191            table_name,
192            selection,
193            assignments,
194        } => {
195            let Schema {
196                column_defs,
197                foreign_keys,
198                ..
199            } = storage
200                .fetch_schema(table_name)?
201                .ok_or_else(|| ExecuteError::TableNotFound(table_name.to_owned()))?;
202
203            let all_columns = column_defs.as_deref().map_or_else(
204                || Rc::from(vec![SCHEMALESS_DOC_COLUMN.to_owned()]),
205                |columns| columns.iter().map(|col_def| col_def.name.clone()).collect(),
206            );
207            let columns_to_update: Vec<String> = assignments
208                .iter()
209                .map(|assignment| assignment.id.clone())
210                .collect();
211
212            let update = Update::new(storage, table_name, assignments, column_defs.as_deref())?;
213
214            let foreign_keys = Rc::new(foreign_keys);
215
216            let rows = fetch(storage, table_name, all_columns, selection.as_ref())?
217                .map(|item| {
218                    let (key, row) = item?;
219                    let row = update.apply(row, foreign_keys.as_ref())?;
220
221                    Ok((key, row))
222                })
223                .collect::<Result<Vec<(Key, Row)>>>()?;
224
225            if let Some(column_defs) = column_defs {
226                let column_validation =
227                    ColumnValidation::SpecifiedColumns(&column_defs, columns_to_update);
228                let rows = rows.iter().map(|(_, row)| row.values.as_slice());
229
230                validate_unique(storage, table_name, &column_validation, rows)?;
231            }
232
233            let num_rows = rows.len();
234            let rows = rows
235                .into_iter()
236                .map(|(key, row)| (key, row.into_values()))
237                .collect();
238
239            storage
240                .insert_data(table_name, rows)
241                .map(|()| Payload::Update(num_rows))
242        }
243        StatementPlan::Delete {
244            table_name,
245            selection,
246        } => delete(storage, table_name, selection.as_ref()),
247
248        //- Selection
249        StatementPlan::Query(query) => select::execute(storage, query),
250        StatementPlan::ShowColumns { table_name } => {
251            let Schema { column_defs, .. } = storage
252                .fetch_schema(table_name)?
253                .ok_or_else(|| ExecuteError::TableNotFound(table_name.to_owned()))?;
254
255            let output: Vec<(String, DataType)> = column_defs
256                .unwrap_or_default()
257                .into_iter()
258                .map(|key| (key.name, key.data_type))
259                .collect();
260
261            Ok(Payload::ShowColumns(output))
262        }
263        StatementPlan::ShowIndexes(table_name) => {
264            let query = QueryPlan::Project(ProjectPlan {
265                projection: ProjectionPlan::SelectItems(vec![SelectItemPlan::Wildcard]),
266                input: ProjectInputPlan::Filter(FilterPlan {
267                    input: FilterInputPlan::Source(SourcePlan::Dictionary(DictionarySourcePlan {
268                        dictionary: Dictionary::GlueIndexes,
269                        alias: TableAliasPlan {
270                            name: "GLUE_INDEXES".to_owned(),
271                            columns: Vec::new(),
272                        },
273                    })),
274                    expr: ExprPlan::BinaryOp {
275                        left: Box::new(ExprPlan::Identifier("TABLE_NAME".to_owned())),
276                        op: BinaryOperator::Eq,
277                        right: Box::new(ExprPlan::Literal(Literal::QuotedString(
278                            table_name.to_owned(),
279                        ))),
280                    },
281                }),
282            });
283
284            let (labels, rows) = query::execute_with_labels(storage, &query, None)?;
285            let rows = rows
286                .map(|row| Ok::<_, Error>(row?.into_values()))
287                .collect::<Result<Vec<_>>>()?;
288
289            if rows.is_empty() {
290                return Err(ExecuteError::TableNotFound(table_name.to_owned()).into());
291            }
292
293            Ok(Payload::Select { labels, rows })
294        }
295        StatementPlan::ShowVariable(variable) => match variable {
296            Variable::Tables => {
297                let query = QueryPlan::Project(ProjectPlan {
298                    projection: ProjectionPlan::SelectItems(vec![SelectItemPlan::Expr {
299                        expr: ExprPlan::Identifier("TABLE_NAME".to_owned()),
300                        label: "TABLE_NAME".to_owned(),
301                    }]),
302                    input: ProjectInputPlan::Source(SourcePlan::Dictionary(DictionarySourcePlan {
303                        dictionary: Dictionary::GlueTables,
304                        alias: TableAliasPlan {
305                            name: "GLUE_TABLES".to_owned(),
306                            columns: Vec::new(),
307                        },
308                    })),
309                });
310
311                let table_names = query::execute(storage, &query, None)?
312                    .map(|row| Ok::<_, Error>(row?.into_values()))
313                    .collect::<Result<Vec<Vec<Value>>>>()?
314                    .iter()
315                    .flat_map(|values| values.iter().map(Into::into))
316                    .collect::<Vec<_>>();
317
318                Ok(Payload::ShowVariable(PayloadVariable::Tables(table_names)))
319            }
320            Variable::Functions => {
321                let mut function_desc: Vec<_> = storage
322                    .fetch_all_functions()?
323                    .iter()
324                    .map(|f| f.to_str())
325                    .collect();
326                function_desc.sort();
327                Ok(Payload::ShowVariable(PayloadVariable::Functions(
328                    function_desc,
329                )))
330            }
331            Variable::Version => {
332                let version = var("CARGO_PKG_VERSION")
333                    .unwrap_or_else(|_| env!("CARGO_PKG_VERSION").to_owned());
334                let payload = Payload::ShowVariable(PayloadVariable::Version(version));
335
336                Ok(payload)
337            }
338        },
339        StatementPlan::CreateFunction {
340            or_replace,
341            name,
342            args,
343            return_,
344        } => insert_function(storage, name, args, *or_replace, return_).map(|()| Payload::Create),
345        StatementPlan::DropFunction { if_exists, names } => {
346            delete_function(storage, names, *if_exists).map(|()| Payload::DropFunction)
347        }
348    }
349}