gluesql_core/
translate.rs

1mod ast_literal;
2mod data_type;
3mod ddl;
4mod error;
5mod expr;
6mod function;
7mod operator;
8mod query;
9
10pub use self::{
11    data_type::translate_data_type,
12    ddl::{translate_column_def, translate_operate_function_arg},
13    error::TranslateError,
14    expr::{translate_expr, translate_order_by_expr},
15    query::{alias_or_name, translate_query, translate_select_item},
16};
17
18use {
19    crate::{
20        ast::{Assignment, ForeignKey, ReferentialAction, Statement, Variable},
21        result::Result,
22    },
23    ddl::translate_alter_table_operation,
24    sqlparser::ast::{
25        Assignment as SqlAssignment, AssignmentTarget as SqlAssignmentTarget,
26        CommentDef as SqlCommentDef, CreateFunctionBody as SqlCreateFunctionBody,
27        CreateIndex as SqlCreateIndex, CreateTable as SqlCreateTable, Delete as SqlDelete,
28        FromTable as SqlFromTable, Ident as SqlIdent, Insert as SqlInsert,
29        ObjectName as SqlObjectName, ObjectType as SqlObjectType,
30        ReferentialAction as SqlReferentialAction, Statement as SqlStatement,
31        TableConstraint as SqlTableConstraint, TableFactor, TableWithJoins,
32    },
33};
34
35pub fn translate(sql_statement: &SqlStatement) -> Result<Statement> {
36    match sql_statement {
37        SqlStatement::Query(query) => translate_query(query).map(Statement::Query),
38        SqlStatement::Insert(SqlInsert {
39            table_name,
40            columns,
41            source,
42            ..
43        }) => {
44            let table_name = translate_object_name(table_name)?;
45            let columns = translate_idents(columns);
46            let source = source
47                .as_deref()
48                .ok_or_else(|| {
49                    TranslateError::DefaultValuesOnInsertNotSupported(table_name.clone()).into()
50                })
51                .and_then(translate_query)?;
52
53            Ok(Statement::Insert {
54                table_name,
55                columns,
56                source,
57            })
58        }
59        SqlStatement::Update {
60            table,
61            assignments,
62            selection,
63            ..
64        } => Ok(Statement::Update {
65            table_name: translate_table_with_join(table)?,
66            assignments: assignments
67                .iter()
68                .map(translate_assignment)
69                .collect::<Result<_>>()?,
70            selection: selection.as_ref().map(translate_expr).transpose()?,
71        }),
72        SqlStatement::Delete(SqlDelete {
73            from, selection, ..
74        }) => {
75            let from = match from {
76                SqlFromTable::WithFromKeyword(from) => from,
77                SqlFromTable::WithoutKeyword(_) => {
78                    return Err(TranslateError::UnreachableOmittingFromInDelete.into());
79                }
80            };
81            let table_name = from
82                .iter()
83                .map(translate_table_with_join)
84                .next()
85                .ok_or(TranslateError::UnreachableEmptyTable)??;
86
87            Ok(Statement::Delete {
88                table_name,
89                selection: selection.as_ref().map(translate_expr).transpose()?,
90            })
91        }
92        SqlStatement::CreateTable(SqlCreateTable {
93            if_not_exists,
94            name,
95            columns,
96            query,
97            engine,
98            constraints,
99            comment,
100            ..
101        }) => {
102            let columns = columns
103                .iter()
104                .map(translate_column_def)
105                .collect::<Result<Vec<_>>>()?;
106
107            let columns = (!columns.is_empty()).then_some(columns);
108
109            let name = translate_object_name(name)?;
110
111            let foreign_keys = constraints
112                .iter()
113                .map(translate_foreign_key)
114                .collect::<Result<Vec<_>>>()?;
115
116            Ok(Statement::CreateTable {
117                if_not_exists: *if_not_exists,
118                name,
119                columns,
120                source: match query {
121                    Some(v) => Some(translate_query(v).map(Box::new)?),
122                    None => None,
123                },
124                engine: engine
125                    .as_ref()
126                    .map(|table_engine| table_engine.name.to_owned()),
127                foreign_keys,
128                comment: comment.as_ref().map(|comment| match comment {
129                    SqlCommentDef::WithEq(comment)
130                    | SqlCommentDef::WithoutEq(comment)
131                    | SqlCommentDef::AfterColumnDefsWithoutEq(comment) => comment.to_owned(),
132                }),
133            })
134        }
135        SqlStatement::AlterTable {
136            name, operations, ..
137        } => {
138            if operations.len() > 1 {
139                return Err(TranslateError::UnsupportedMultipleAlterTableOperations.into());
140            }
141
142            let operation = operations
143                .iter()
144                .next()
145                .ok_or(TranslateError::UnreachableEmptyAlterTableOperation)?;
146
147            Ok(Statement::AlterTable {
148                name: translate_object_name(name)?,
149                operation: translate_alter_table_operation(operation)?,
150            })
151        }
152        SqlStatement::Drop {
153            object_type: SqlObjectType::Table,
154            if_exists,
155            names,
156            cascade,
157            ..
158        } => Ok(Statement::DropTable {
159            if_exists: *if_exists,
160            names: names
161                .iter()
162                .map(translate_object_name)
163                .collect::<Result<Vec<_>>>()?,
164            cascade: *cascade,
165        }),
166        SqlStatement::DropFunction {
167            if_exists,
168            func_desc,
169            ..
170        } => Ok(Statement::DropFunction {
171            if_exists: *if_exists,
172            names: func_desc
173                .iter()
174                .map(|v| translate_object_name(&v.name))
175                .collect::<Result<Vec<_>>>()?,
176        }),
177        SqlStatement::CreateIndex(SqlCreateIndex {
178            name,
179            table_name,
180            columns,
181            ..
182        }) => {
183            if columns.len() > 1 {
184                return Err(TranslateError::CompositeIndexNotSupported.into());
185            }
186
187            let Some(name) = name else {
188                return Err(TranslateError::UnsupportedUnnamedIndex.into());
189            };
190
191            let name = translate_object_name(name)?;
192
193            if name.to_uppercase() == "PRIMARY" {
194                return Err(TranslateError::ReservedIndexName(name).into());
195            };
196
197            Ok(Statement::CreateIndex {
198                name,
199                table_name: translate_object_name(table_name)?,
200                column: translate_order_by_expr(&columns[0])?,
201            })
202        }
203        SqlStatement::Drop {
204            object_type: SqlObjectType::Index,
205            names,
206            ..
207        } => {
208            if names.len() > 1 {
209                return Err(TranslateError::TooManyParamsInDropIndex.into());
210            }
211
212            let object_name = &names[0].0;
213            if object_name.len() != 2 {
214                return Err(TranslateError::InvalidParamsInDropIndex.into());
215            }
216
217            let table_name = object_name[0].value.to_owned();
218            let name = object_name[1].value.to_owned();
219
220            if name.to_uppercase() == "PRIMARY" {
221                return Err(TranslateError::CannotDropPrimary.into());
222            };
223
224            Ok(Statement::DropIndex { name, table_name })
225        }
226        SqlStatement::StartTransaction { .. } => Ok(Statement::StartTransaction),
227        SqlStatement::Commit { .. } => Ok(Statement::Commit),
228        SqlStatement::Rollback { .. } => Ok(Statement::Rollback),
229        SqlStatement::ShowTables {
230            filter: None,
231            db_name: None,
232            ..
233        } => Ok(Statement::ShowVariable(Variable::Tables)),
234        SqlStatement::ShowFunctions { filter: None } => {
235            Ok(Statement::ShowVariable(Variable::Functions))
236        }
237        SqlStatement::ShowVariable { variable } => match (variable.len(), variable.first()) {
238            (1, Some(keyword)) => match keyword.value.to_uppercase().as_str() {
239                "VERSION" => Ok(Statement::ShowVariable(Variable::Version)),
240                v => Err(TranslateError::UnsupportedShowVariableKeyword(v.to_owned()).into()),
241            },
242            (3, Some(keyword)) => match keyword.value.to_uppercase().as_str() {
243                "INDEXES" => match variable.get(2) {
244                    Some(tablename) => Ok(Statement::ShowIndexes(tablename.value.to_owned())),
245                    _ => Err(TranslateError::UnsupportedShowVariableStatement(
246                        sql_statement.to_string(),
247                    )
248                    .into()),
249                },
250                _ => Err(TranslateError::UnsupportedShowVariableStatement(
251                    sql_statement.to_string(),
252                )
253                .into()),
254            },
255            _ => Err(
256                TranslateError::UnsupportedShowVariableStatement(sql_statement.to_string()).into(),
257            ),
258        },
259        SqlStatement::ShowColumns { table_name, .. } => Ok(Statement::ShowColumns {
260            table_name: translate_object_name(table_name)?,
261        }),
262        SqlStatement::CreateFunction {
263            or_replace,
264            name,
265            args,
266            function_body: Some(SqlCreateFunctionBody::Return(return_)),
267            ..
268        } => {
269            let args = args
270                .as_ref()
271                .map(|args| {
272                    args.iter()
273                        .map(translate_operate_function_arg)
274                        .collect::<Result<Vec<_>>>()
275                })
276                .transpose()?;
277            Ok(Statement::CreateFunction {
278                or_replace: *or_replace,
279                name: translate_object_name(name)?,
280                args: args.unwrap_or_default(),
281                return_: translate_expr(return_)?,
282            })
283        }
284        SqlStatement::CreateFunction { .. } => {
285            Err(TranslateError::UnsupportedEmptyFunctionBody.into())
286        }
287        _ => Err(TranslateError::UnsupportedStatement(sql_statement.to_string()).into()),
288    }
289}
290
291pub fn translate_assignment(sql_assignment: &SqlAssignment) -> Result<Assignment> {
292    let SqlAssignment { target, value } = sql_assignment;
293
294    let id = match target {
295        SqlAssignmentTarget::Tuple(_) => {
296            return Err(TranslateError::TupleAssignmentOnUpdateNotSupported(
297                sql_assignment.to_string(),
298            )
299            .into());
300        }
301        SqlAssignmentTarget::ColumnName(SqlObjectName(id)) => id,
302    };
303
304    if id.len() > 1 {
305        return Err(
306            TranslateError::CompoundIdentOnUpdateNotSupported(sql_assignment.to_string()).into(),
307        );
308    }
309
310    Ok(Assignment {
311        id: id
312            .first()
313            .ok_or(TranslateError::UnreachableEmptyIdent)?
314            .value
315            .to_owned(),
316        value: translate_expr(value)?,
317    })
318}
319
320fn translate_table_with_join(table: &TableWithJoins) -> Result<String> {
321    if !table.joins.is_empty() {
322        return Err(TranslateError::JoinOnUpdateNotSupported.into());
323    }
324    match &table.relation {
325        TableFactor::Table { name, .. } => translate_object_name(name),
326        t => Err(TranslateError::UnsupportedTableFactor(t.to_string()).into()),
327    }
328}
329
330fn translate_object_name(sql_object_name: &SqlObjectName) -> Result<String> {
331    let sql_object_name = &sql_object_name.0;
332    if sql_object_name.len() > 1 {
333        let compound_object_name = translate_idents(sql_object_name).join(".");
334        return Err(TranslateError::CompoundObjectNotSupported(compound_object_name).into());
335    }
336
337    sql_object_name
338        .first()
339        .map(|v| v.value.to_owned())
340        .ok_or_else(|| TranslateError::UnreachableEmptyObject.into())
341}
342
343pub fn translate_idents(idents: &[SqlIdent]) -> Vec<String> {
344    idents.iter().map(|v| v.value.to_owned()).collect()
345}
346
347pub fn translate_referential_action(
348    action: &Option<SqlReferentialAction>,
349) -> Result<ReferentialAction> {
350    use SqlReferentialAction::*;
351
352    let action = action.unwrap_or(NoAction);
353
354    match action {
355        NoAction | Restrict => Ok(ReferentialAction::NoAction),
356        _ => Err(TranslateError::UnsupportedConstraint(action.to_string()).into()),
357    }
358}
359
360pub fn translate_foreign_key(table_constraint: &SqlTableConstraint) -> Result<ForeignKey> {
361    match table_constraint {
362        SqlTableConstraint::ForeignKey {
363            name,
364            columns,
365            foreign_table,
366            referred_columns,
367            on_delete,
368            on_update,
369            ..
370        } => {
371            let referencing_column_name = columns.first().map(|i| i.value.clone()).ok_or(
372                TranslateError::UnreachableForeignKeyColumns(
373                    columns.iter().map(|i| i.to_string()).collect::<String>(),
374                ),
375            )?;
376
377            let referenced_column_name = referred_columns
378                .first()
379                .ok_or(TranslateError::UnreachableForeignKeyColumns(
380                    columns.iter().map(|i| i.to_string()).collect::<String>(),
381                ))?
382                .value
383                .clone();
384
385            let referenced_table_name = translate_object_name(foreign_table)?;
386
387            let name = match name {
388                Some(name) => name.value.clone(),
389                None => {
390                    format!(
391                        "FK_{referencing_column_name}-{referenced_table_name}_{referenced_column_name}"
392                    )
393                }
394            };
395
396            Ok(ForeignKey {
397                name,
398                referencing_column_name,
399                referenced_table_name,
400                referenced_column_name,
401                on_delete: translate_referential_action(on_delete)?,
402                on_update: translate_referential_action(on_update)?,
403            })
404        }
405        _ => Err(TranslateError::UnsupportedConstraint(table_constraint.to_string()).into()),
406    }
407}
408
409#[cfg(test)]
410mod tests {
411    use {super::*, crate::parse_sql::parse};
412
413    #[test]
414    fn statement() {
415        let sql = "INSERT INTO Foo DEFAULT VALUES";
416        let actual = parse(sql).and_then(|parsed| translate(&parsed[0]));
417        let expected =
418            Err(TranslateError::DefaultValuesOnInsertNotSupported("Foo".to_owned()).into());
419
420        assert_eq!(actual, expected);
421    }
422
423    #[test]
424    fn test_tuple_assignment_on_update_not_supported() {
425        let sql = "UPDATE Foo SET (a, b) = (1, 2)";
426        let actual = parse(sql).and_then(|parsed| translate(&parsed[0]));
427        let expected = Err(TranslateError::TupleAssignmentOnUpdateNotSupported(
428            "(a, b) = (1, 2)".to_owned(),
429        )
430        .into());
431
432        assert_eq!(actual, expected);
433    }
434}