LCOV - code coverage report
Current view: top level - dashql-core/src/formatter - create.rs Coverage Total Hit
Test: DashQL d805a4b Lines: 96.1 % 230 221
Test Date: 2026-11-09 20:22:13

            Line data    Source code
       1              : //! CREATE, DROP, ATTACH and SET statements.
       2              : 
       3              : use dashql_parser::attrs;
       4              : use super::{FmtReg, Formatter, FormattingJoinPolicy, K};
       5              : use dashql_buffers::parser::{Node, NodeType, OnCommitOption, TempType};
       6              : 
       7              : impl<'a> Formatter<'a> {
       8          105 :     pub(super) fn format_temp_type(&self, node: &'a Node) -> FmtReg {
       9          105 :         let f = &self.fmt;
      10          105 :         match TempType(super::value_u8(node)) {
      11           51 :             TempType::DEFAULT | TempType::LOCAL => f.text("temporary"),
      12            3 :             TempType::GLOBAL => f.text("global temporary"),
      13            3 :             TempType::UNLOGGED => f.text("unlogged"),
      14           48 :             TempType::NONE => f.empty(),
      15            0 :             _ => self.format_unimplemented(node),
      16              :         }
      17          105 :     }
      18              : 
      19           33 :     pub(super) fn format_on_commit_option(&self, node: &'a Node) -> FmtReg {
      20           33 :         let f = &self.fmt;
      21           33 :         match OnCommitOption(super::value_u8(node)) {
      22           15 :             OnCommitOption::DROP => f.text("on commit drop"),
      23            9 :             OnCommitOption::DELETE_ROWS => f.text("on commit delete rows"),
      24            9 :             OnCommitOption::PRESERVE_ROWS => f.text("on commit preserve rows"),
      25            0 :             OnCommitOption::NOOP => f.empty(),
      26            0 :             _ => self.format_unimplemented(node),
      27              :         }
      28           33 :     }
      29              : 
      30          335 :     pub(super) fn format_create(&self, node_id: usize) -> FmtReg {
      31          335 :         let f = &self.fmt;
      32          335 :         let node: &'a Node = &self.ast[node_id];
      33          335 :         if !self.node_states[node_id].is_statement_root {
      34            1 :             return self.format_unimplemented(node);
      35          334 :         }
      36          334 :         let [name, elements, if_not_exists, temp, on_commit] = attrs!(self.children(node), [
      37              :                 K::SQL_CREATE_TABLE_NAME,
      38              :                 K::SQL_CREATE_TABLE_ELEMENTS,
      39              :                 K::SQL_CREATE_TABLE_IF_NOT_EXISTS,
      40              :                 K::SQL_CREATE_TABLE_TEMP,
      41              :                 K::SQL_CREATE_TABLE_ON_COMMIT,
      42              :             ],
      43              :         );
      44          334 :         let (Some(name), Some(elements)) = (name, elements) else { return self.format_unimplemented(node) };
      45              : 
      46          334 :         let mut header_parts = vec![f.text("create ")];
      47          334 :         if let Some(temp) = temp {
      48           33 :             header_parts.push(self.reg(temp));
      49           33 :             header_parts.push(f.text(" "));
      50          301 :         }
      51          334 :         header_parts.push(f.text("table "));
      52          334 :         if if_not_exists.is_some() {
      53           15 :             header_parts.push(f.text("if not exists "));
      54          319 :         }
      55          334 :         header_parts.push(self.reg(name));
      56          334 :         let header = f.concat(&header_parts);
      57              : 
      58          334 :         let mut table_elements = f.empty();
      59          334 :         if elements.children_count() > 0 {
      60          334 :             let mut parts = Vec::with_capacity(elements.children_count() as usize);
      61         1387 :             for element in self.children(elements) {
      62         1387 :                 let reg = self.reg(element);
      63         1387 :                 if reg == 0 {
      64            0 :                     return self.format_unimplemented(node);
      65         1387 :                 }
      66         1387 :                 parts.push(reg);
      67              :             }
      68          334 :             let (inline_separator, break_separator) = self.comma_separators();
      69          334 :             table_elements = f.join(&parts, inline_separator, break_separator, None, false);
      70            0 :         }
      71              : 
      72          334 :         let element_block = if elements.children_count() > 0 { f.parenthesized(table_elements) } else { f.text("()") };
      73          334 :         let mut statement = f.concat(&[header, f.text(" "), element_block]);
      74          334 :         if let Some(on_commit) = on_commit {
      75           24 :             statement = f.concat(&[statement, f.text(" "), self.reg(on_commit)]);
      76          310 :         }
      77          334 :         statement
      78          335 :     }
      79              : 
      80           39 :     pub(super) fn format_create_as(&self, node: &'a Node) -> FmtReg {
      81           39 :         let f = &self.fmt;
      82           39 :         let [name, columns, if_not_exists, temp, statement, with_data, on_commit] = attrs!(self.children(node), [
      83              :                 K::SQL_CREATE_AS_NAME,
      84              :                 K::SQL_CREATE_AS_COLUMNS,
      85              :                 K::SQL_CREATE_AS_IF_NOT_EXISTS,
      86              :                 K::SQL_CREATE_AS_TEMP,
      87              :                 K::SQL_CREATE_AS_STATEMENT,
      88              :                 K::SQL_CREATE_AS_WITH_DATA,
      89              :                 K::SQL_CREATE_AS_ON_COMMIT,
      90              :             ],
      91              :         );
      92           39 :         let (Some(name), Some(statement)) = (name, statement) else { return self.format_unimplemented(node) };
      93              : 
      94           39 :         let mut parts = vec![f.text("create ")];
      95           39 :         if let Some(temp) = temp {
      96           15 :             parts.push(self.reg(temp));
      97           15 :             parts.push(f.text(" "));
      98           24 :         }
      99           39 :         parts.push(f.text("table "));
     100           39 :         if if_not_exists.is_some() {
     101           15 :             parts.push(f.text("if not exists "));
     102           33 :         }
     103           39 :         parts.push(self.reg(name));
     104           39 :         if let Some(columns) = columns.filter(|c| c.children_count() > 0) {
     105            6 :             parts.push(f.parenthesized(self.reg(columns)));
     106           33 :         }
     107           39 :         if let Some(on_commit) = on_commit {
     108            9 :             parts.push(f.text(" "));
     109            9 :             parts.push(self.reg(on_commit));
     110           30 :         }
     111           39 :         parts.push(f.text(" as"));
     112              :         // The query follows on the same line only if the whole statement fits, like the query of a view
     113           39 :         let header = f.concat(&parts);
     114           39 :         let body = self.space_join(&[header, self.reg(statement)], FormattingJoinPolicy::BreakAllOrNone, false);
     115           39 :         match with_data {
     116            9 :             Some(with_data) => {
     117            9 :                 let include_data = with_data.node_type() == NodeType::BOOL && with_data.children_begin_or_value() != 0;
     118            9 :                 f.concat(&[body, f.text(if include_data { " with data" } else { " with no data" })])
     119              :             }
     120           30 :             None => body,
     121              :         }
     122           39 :     }
     123              : 
     124           24 :     pub(super) fn format_view(&self, node: &'a Node) -> FmtReg {
     125           24 :         let f = &self.fmt;
     126           24 :         let [name, columns, or_replace, temp, statement] = attrs!(self.children(node), [K::SQL_VIEW_NAME, K::SQL_VIEW_COLUMNS, K::SQL_VIEW_OR_REPLACE, K::SQL_VIEW_TEMP, K::SQL_VIEW_STATEMENT],
     127              :         );
     128           24 :         let (Some(name), Some(statement)) = (name, statement) else { return self.format_unimplemented(node) };
     129              : 
     130           24 :         let mut parts = vec![f.text("create ")];
     131           24 :         if or_replace.is_some() {
     132            6 :             parts.push(f.text("or replace "));
     133           18 :         }
     134           24 :         if let Some(temp) = temp {
     135            6 :             parts.push(self.reg(temp));
     136            6 :             parts.push(f.text(" "));
     137           18 :         }
     138           24 :         parts.push(f.text("view "));
     139           24 :         parts.push(self.reg(name));
     140           24 :         if let Some(columns) = columns.filter(|c| c.children_count() > 0) {
     141            3 :             parts.push(f.parenthesized(self.reg(columns)));
     142           21 :         }
     143           24 :         parts.push(f.text(" as"));
     144           24 :         let header = f.concat(&parts);
     145           24 :         self.space_join(&[header, self.reg(statement)], FormattingJoinPolicy::BreakAllOrNone, false)
     146           24 :     }
     147              : 
     148           57 :     pub(super) fn format_function_param(&self, node: &'a Node) -> FmtReg {
     149           57 :         let f = &self.fmt;
     150           57 :         let [name, type_node] = attrs!(self.children(node), [K::SQL_FUNCTION_PARAM_NAME, K::SQL_FUNCTION_PARAM_TYPE]);
     151           57 :         let (Some(name), Some(type_node)) = (name, type_node) else { return self.format_unimplemented(node) };
     152           57 :         f.concat(&[self.reg(name), f.text(" "), self.reg(type_node)])
     153           57 :     }
     154              : 
     155           45 :     pub(super) fn format_create_function(&self, node: &'a Node) -> FmtReg {
     156           45 :         let f = &self.fmt;
     157           45 :         let [name, params, returns, aggregate] = attrs!(self.children(node), [
     158              :                 K::SQL_CREATE_FUNCTION_NAME,
     159              :                 K::SQL_CREATE_FUNCTION_PARAMS,
     160              :                 K::SQL_CREATE_FUNCTION_RETURNS,
     161              :                 K::SQL_CREATE_FUNCTION_IS_AGGREGATE,
     162              :             ],
     163              :         );
     164           45 :         let (Some(name), Some(returns)) = (name, returns) else { return self.format_unimplemented(node) };
     165           45 :         let kind = if aggregate.is_some() { "create aggregate " } else { "create function " };
     166           45 :         let params_reg = params.map(|p| self.reg(p)).unwrap_or(0);
     167           45 :         let params_reg = if params_reg == 0 { f.text("()") } else { f.parenthesized(params_reg) };
     168           45 :         f.concat(&[f.text(kind), self.reg(name), params_reg, f.text(" returns "), self.reg(returns)])
     169           45 :     }
     170              : 
     171           42 :     pub(super) fn format_drop(&self, node: &'a Node, table: bool) -> FmtReg {
     172           42 :         let f = &self.fmt;
     173           42 :         let [name, if_exists] = attrs!(self.children(node), [K::SQL_DROP_NAME, K::SQL_DROP_IF_EXISTS]);
     174           42 :         let Some(name) = name else { return self.format_unimplemented(node) };
     175           42 :         let object = if table { "table " } else { "view " };
     176           42 :         let if_exists_reg = if if_exists.is_some() { f.text("if exists ") } else { f.empty() };
     177           42 :         f.concat(&[f.text("drop "), f.text(object), if_exists_reg, self.reg(name)])
     178           42 :     }
     179              : 
     180           39 :     pub(super) fn format_schema_statement(&self, node: &'a Node) -> FmtReg {
     181           39 :         let f = &self.fmt;
     182           39 :         let [name, condition, cascade, new_name] =
     183           39 :             attrs!(self.children(node), [K::SQL_SCHEMA_NAME, K::SQL_SCHEMA_IF, K::SQL_SCHEMA_CASCADE, K::SQL_SCHEMA_NEW_NAME]);
     184           39 :         let Some(name) = name else { return self.format_unimplemented(node) };
     185           39 :         match (node.node_type(), new_name) {
     186              :             (NodeType::OBJECT_SQL_CREATE_SCHEMA, _) => {
     187            9 :                 let condition = if condition.is_some() { f.text("if not exists ") } else { f.empty() };
     188            9 :                 f.concat(&[f.text("create schema "), condition, self.reg(name)])
     189              :             }
     190              :             (NodeType::OBJECT_SQL_DROP_SCHEMA, _) => {
     191           21 :                 let condition = if condition.is_some() { f.text("if exists ") } else { f.empty() };
     192           21 :                 f.concat(&[f.text("drop schema "), condition, self.reg(name), self.format_drop_behavior(cascade)])
     193              :             }
     194            9 :             (NodeType::OBJECT_SQL_ALTER_SCHEMA, Some(new_name)) => f.concat(&[f.text("alter schema "), self.reg(name), f.text(" rename to "), self.reg(new_name)]),
     195            0 :             _ => self.format_unimplemented(node),
     196              :         }
     197           39 :     }
     198              : 
     199           78 :     fn format_drop_behavior(&self, cascade: Option<&'a Node>) -> FmtReg {
     200           78 :         let f = &self.fmt;
     201           33 :         match cascade {
     202           33 :             Some(cascade) if cascade.children_begin_or_value() != 0 => f.text(" cascade"),
     203           12 :             Some(_) => f.text(" restrict"),
     204           45 :             None => f.empty(),
     205              :         }
     206           78 :     }
     207              : 
     208          168 :     pub(super) fn format_alter_table(&self, node: &'a Node, statement: &'static str) -> FmtReg {
     209          168 :         let f = &self.fmt;
     210          168 :         let [name, if_exists, actions] = attrs!(self.children(node), [K::SQL_ALTER_TABLE_NAME, K::SQL_ALTER_TABLE_IF_EXISTS, K::SQL_ALTER_TABLE_ACTIONS]);
     211          168 :         let (Some(name), Some(actions)) = (name, actions) else { return self.format_unimplemented(node) };
     212          168 :         let if_exists_reg = if if_exists.is_some() { f.text("if exists ") } else { f.empty() };
     213          168 :         f.concat(&[f.text(statement), if_exists_reg, self.reg(name), f.text(" "), self.reg(actions)])
     214          168 :     }
     215              : 
     216          186 :     pub(super) fn format_alter_table_action(&self, node: &'a Node) -> FmtReg {
     217          186 :         let f = &self.fmt;
     218          186 :         let [column, condition, condition_after, new_name, new_table, column_type, cascade, using] = attrs!(self.children(node), [
     219              :                 K::SQL_ALTER_ACTION_COLUMN,
     220              :                 K::SQL_ALTER_ACTION_IF,
     221              :                 K::SQL_ALTER_ACTION_IF_AFTER,
     222              :                 K::SQL_ALTER_ACTION_NEW_NAME,
     223              :                 K::SQL_ALTER_ACTION_NEW_TABLE,
     224              :                 K::SQL_ALTER_ACTION_TYPE,
     225              :                 K::SQL_ALTER_ACTION_CASCADE,
     226              :                 K::SQL_ALTER_ACTION_USING,
     227              :             ],
     228              :         );
     229          186 :         match (node.node_type(), column, new_name, new_table, column_type) {
     230           36 :             (NodeType::OBJECT_SQL_ALTER_ADD_COLUMN, Some(column), _, _, _) => {
     231           36 :                 let condition = if condition.is_some() { f.text("if not exists ") } else { f.empty() };
     232           36 :                 f.concat(&[f.text("add column "), condition, self.reg(column)])
     233              :             }
     234           57 :             (NodeType::OBJECT_SQL_ALTER_DROP_COLUMN, Some(column), _, _, _) => {
     235              :                 // Trino writes the condition before the column, Hyper after it
     236           57 :                 let condition = if condition.is_some() { f.text("if exists ") } else { f.empty() };
     237           57 :                 let condition_after = if condition_after.is_some() { f.text(" if exists") } else { f.empty() };
     238           57 :                 f.concat(&[f.text("drop column "), condition, self.reg(column), condition_after, self.format_drop_behavior(cascade)])
     239              :             }
     240           21 :             (NodeType::OBJECT_SQL_ALTER_RENAME_COLUMN, Some(column), Some(new_name), _, _) => {
     241           21 :                 f.concat(&[f.text("rename column "), self.reg(column), f.text(" to "), self.reg(new_name)])
     242              :             }
     243           27 :             (NodeType::OBJECT_SQL_ALTER_RENAME_TABLE, _, _, Some(new_table), _) => f.concat(&[f.text("rename to "), self.reg(new_table)]),
     244           18 :             (NodeType::OBJECT_SQL_ALTER_SET_SCHEMA, _, Some(new_name), _, _) => f.concat(&[f.text("set schema "), self.reg(new_name)]),
     245           27 :             (NodeType::OBJECT_SQL_ALTER_COLUMN_TYPE, Some(column), _, _, Some(column_type)) => {
     246           27 :                 let using = match using {
     247            9 :                     Some(using) => f.concat(&[f.text(" using "), self.reg(using)]),
     248           18 :                     None => f.empty(),
     249              :                 };
     250           27 :                 f.concat(&[f.text("alter column "), self.reg(column), f.text(" set data type "), self.reg(column_type), using])
     251              :             }
     252            0 :             _ => self.format_unimplemented(node),
     253              :         }
     254          186 :     }
     255              : 
     256           18 :     pub(super) fn format_attach_database_option(&self, node: &'a Node) -> FmtReg {
     257           18 :         let f = &self.fmt;
     258           18 :         let [key, value] = attrs!(self.children(node), [K::SQL_GENERIC_OPTION_KEY, K::SQL_GENERIC_OPTION_VALUE]);
     259           18 :         let (Some(key), Some(value)) = (key, value) else { return self.format_unimplemented(node) };
     260           18 :         f.concat(&[self.reg(key), f.text(" = "), self.reg(value)])
     261           18 :     }
     262              : 
     263           18 :     pub(super) fn format_attach_database(&self, node: &'a Node) -> FmtReg {
     264           18 :         let f = &self.fmt;
     265           18 :         let [path, alias, local, options] = attrs!(self.children(node), [
     266              :                 K::SQL_ATTACH_DATABASE_PATH,
     267              :                 K::SQL_ATTACH_DATABASE_ALIAS,
     268              :                 K::SQL_ATTACH_DATABASE_LOCAL,
     269              :                 K::SQL_ATTACH_DATABASE_OPTIONS,
     270              :             ],
     271              :         );
     272           18 :         let (Some(path), Some(alias)) = (path, alias) else { return self.format_unimplemented(node) };
     273           18 :         let mut parts = vec![
     274           18 :             f.text(if local.is_some() { "attach local database " } else { "attach database " }),
     275           18 :             self.reg(path),
     276           18 :             f.text(" as "),
     277           18 :             self.reg(alias),
     278              :         ];
     279           18 :         if let Some(options) = options {
     280            9 :             parts.push(f.text(" with "));
     281            9 :             parts.push(f.parenthesized(self.reg(options)));
     282            9 :         }
     283           18 :         f.concat(&parts)
     284           18 :     }
     285              : 
     286          201 :     pub(super) fn format_vararg_field(&self, node: &'a Node) -> FmtReg {
     287          201 :         let f = &self.fmt;
     288          201 :         let [key, value] = attrs!(self.children(node), [K::EXT_VARARG_FIELD_KEY, K::EXT_VARARG_FIELD_VALUE]);
     289          201 :         let (Some(key), Some(value)) = (key, value) else { return self.format_unimplemented(node) };
     290          201 :         f.concat(&[self.reg(key), f.text(" = "), self.reg(value)])
     291          201 :     }
     292              : 
     293              :     /// The values of an array in brackets, with one value per line if they don't fit on one. A single value stays next
     294              :     /// to the brackets, so that `[(` opens an object that breaks.
     295           48 :     pub(super) fn format_vararg_array(&self, node: &'a Node) -> FmtReg {
     296           48 :         let f = &self.fmt;
     297           48 :         let [values] = attrs!(self.children(node), [K::EXT_VARARG_ARRAY_VALUES]);
     298           48 :         let Some(values) = values.filter(|v| v.node_type() == NodeType::ARRAY) else {
     299            0 :             return self.format_unimplemented(node);
     300              :         };
     301           48 :         if values.children_count() == 0 {
     302            9 :             return f.text("[]");
     303           39 :         }
     304           93 :         let items: Vec<FmtReg> = self.children(values).iter().map(|v| self.reg(v)).filter(|&r| r != 0).collect();
     305           39 :         if items.is_empty() {
     306            0 :             return f.text("[]");
     307           39 :         }
     308           39 :         if let [item] = items[..] {
     309           15 :             return f.concat(&[f.text("["), item, f.text("]")]);
     310           24 :         }
     311           24 :         let (inline_separator, break_separator) = self.comma_separators();
     312           24 :         f.bracketed_list(f.join(&items, inline_separator, break_separator, None, false))
     313           48 :     }
     314              : 
     315           24 :     pub(super) fn format_set(&self, node: &'a Node) -> FmtReg {
     316           24 :         let f = &self.fmt;
     317           24 :         let [varargs] = attrs!(self.children(node), [K::EXT_SET_VARARGS]);
     318           24 :         let Some(varargs) = varargs else { return self.format_unimplemented(node) };
     319           24 :         f.concat(&[f.text("set "), self.reg(varargs)])
     320           24 :     }
     321              : }
        

Generated by: LCOV version 2.0-1