diff --git a/crates/squawk_fmt/src/fmt.rs b/crates/squawk_fmt/src/fmt.rs index 29aca858..ca49fdea 100644 --- a/crates/squawk_fmt/src/fmt.rs +++ b/crates/squawk_fmt/src/fmt.rs @@ -13,14 +13,23 @@ use tiny_pretty::{LineBreak, PrintOptions, print}; fn build_source_file(source_file: &ast::SourceFile) -> Doc<'_> { let mut doc = Doc::nil(); + let mut previous_was_stmt = false; for el in source_file.syntax().children_with_tokens() { match el { rowan::NodeOrToken::Node(node) => { if let Some(stmt) = ast::Stmt::cast(node) { + let is_empty_stmt = matches!(&stmt, ast::Stmt::EmptyStmt(_)); + if previous_was_stmt && !is_empty_stmt { + doc = doc.append(Doc::empty_line()); + } doc = doc.append(build_stmt(stmt)); + previous_was_stmt = !is_empty_stmt; + } else { + previous_was_stmt = false; } } rowan::NodeOrToken::Token(token) => { + previous_was_stmt = false; if token.kind() == SyntaxKind::COMMENT { doc = doc.append(Doc::text(token.text().to_string())); } else if token.kind() == SyntaxKind::WHITESPACE { @@ -218,32 +227,26 @@ fn build_rollback<'a>(rollback: ast::Rollback) -> Doc<'a> { } fn build_prepare<'a>(prepare: &ast::Prepare) -> Doc<'a> { - let mut header_body = Doc::nil(); - let mut has_header_body = false; - if let Some(name) = prepare.name() { - has_header_body = true; - header_body = header_body - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } + let mut header_body = prepare + .name() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); if let Some(params) = prepare.param_list() { - has_header_body = true; - header_body = header_body - .append(leading_comments(params.syntax())) - .append(build_function_param_list(params)); + let params = leading_comments(params.syntax()).append(build_function_param_list(params)); + header_body = Some(match header_body { + Some(header_body) => header_body.append(params), + None => params, + }); } if let Some(as_token) = prepare.as_token() { - if has_header_body { - header_body = header_body.append(Doc::line_or_space()); - } - has_header_body = true; - header_body = header_body - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); + let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); + header_body = Some(match header_body { + Some(header_body) => header_body.append(Doc::line_or_space()).append(as_doc), + None => as_doc, + }); } let mut header = Doc::text("prepare"); - if has_header_body { + if let Some(header_body) = header_body { header = header.append(Doc::line_or_space().append(header_body).nest(2)); } let mut doc = header.group(); @@ -1902,14 +1905,7 @@ fn build_function_param_list<'a>(params: ast::ParamList) -> Doc<'a> { let mut body = if let Some(star) = params.star_token() { leading_comments_token(&star).append(Doc::text("*")) } else { - let param_docs = params.params().map(|param| { - let syntax = param.syntax().clone(); - ( - leading_comments(param.syntax()).append(build_function_param(param)), - syntax, - ) - }); - build_comma_separated_docs(param_docs).unwrap_or_else(Doc::nil) + build_function_params(params.params()).unwrap_or_else(Doc::nil) }; if let Some(r_paren) = params.r_paren_token() { body = body.append(comments_before(r_paren)); @@ -1917,6 +1913,16 @@ fn build_function_param_list<'a>(params: ast::ParamList) -> Doc<'a> { doc.append(wrap_body(body)).append(Doc::text(")")) } +fn build_function_params<'a>(params: impl Iterator) -> Option> { + build_comma_separated_docs(params.map(|param| { + let syntax = param.syntax().clone(); + ( + leading_comments(&syntax).append(build_function_param(param)), + syntax, + ) + })) +} + fn build_function_param<'a>(param: ast::Param) -> Doc<'a> { let mut doc = Doc::nil(); if let Some(mode) = param.mode() { @@ -2050,7 +2056,7 @@ fn build_function_option<'a>(option: ast::FuncOption) -> Doc<'a> { } ast::FuncOption::SetFuncOption(option) => option .set_config_param() - .map(build_set_config_param) + .map(|set| build_set_config_param(&set)) .unwrap_or_else(Doc::nil), ast::FuncOption::SupportFuncOption(option) => { let mut doc = Doc::text("support"); @@ -3347,136 +3353,140 @@ fn build_stmt<'a>(stmt: ast::Stmt) -> Doc<'a> { ast::Stmt::Truncate(stmt) => build_truncate(&stmt), ast::Stmt::Update(stmt) => build_update(&stmt), ast::Stmt::Values(stmt) => build_values(&stmt), - ast::Stmt::AlterAggregate(_) => todo!(), - ast::Stmt::AlterCollation(_) => todo!(), - ast::Stmt::AlterConversion(_) => todo!(), - ast::Stmt::AlterDatabase(_) => todo!(), - ast::Stmt::AlterDefaultPrivileges(_) => todo!(), - ast::Stmt::AlterDomain(_) => todo!(), - ast::Stmt::AlterEventTrigger(_) => todo!(), - ast::Stmt::AlterExtension(_) => todo!(), - ast::Stmt::AlterForeignDataWrapper(_) => todo!(), - ast::Stmt::AlterForeignTable(_) => todo!(), - ast::Stmt::AlterFunction(_) => todo!(), - ast::Stmt::AlterGroup(_) => todo!(), - ast::Stmt::AlterIndex(_) => todo!(), - ast::Stmt::AlterLanguage(_) => todo!(), - ast::Stmt::AlterLargeObject(_) => todo!(), - ast::Stmt::AlterMaterializedView(_) => todo!(), - ast::Stmt::AlterOperator(_) => todo!(), - ast::Stmt::AlterOperatorClass(_) => todo!(), - ast::Stmt::AlterOperatorFamily(_) => todo!(), - ast::Stmt::AlterPolicy(_) => todo!(), - ast::Stmt::AlterProcedure(_) => todo!(), - ast::Stmt::AlterPropertyGraph(_) => todo!(), - ast::Stmt::AlterRole(_) => todo!(), - ast::Stmt::AlterRoutine(_) => todo!(), - ast::Stmt::AlterRule(_) => todo!(), - ast::Stmt::AlterSchema(_) => todo!(), - ast::Stmt::AlterSequence(_) => todo!(), - ast::Stmt::AlterServer(_) => todo!(), - ast::Stmt::AlterStatistics(_) => todo!(), - ast::Stmt::AlterSystem(_) => todo!(), - ast::Stmt::AlterTable(_) => todo!(), - ast::Stmt::AlterTablespace(_) => todo!(), - ast::Stmt::AlterTextSearchConfiguration(_) => todo!(), - ast::Stmt::AlterTextSearchDictionary(_) => todo!(), - ast::Stmt::AlterTextSearchParser(_) => todo!(), - ast::Stmt::AlterTextSearchTemplate(_) => todo!(), - ast::Stmt::AlterTrigger(_) => todo!(), - ast::Stmt::AlterType(_) => todo!(), - ast::Stmt::AlterUser(_) => todo!(), - ast::Stmt::AlterUserMapping(_) => todo!(), - ast::Stmt::AlterView(_) => todo!(), + ast::Stmt::AlterAggregate(stmt) => build_alter_aggregate(&stmt), + ast::Stmt::AlterCollation(stmt) => build_alter_collation(&stmt), + ast::Stmt::AlterConversion(stmt) => build_alter_conversion(&stmt), + ast::Stmt::AlterDatabase(stmt) => build_alter_database(&stmt), + ast::Stmt::AlterDefaultPrivileges(stmt) => build_alter_default_privileges(&stmt), + ast::Stmt::AlterDomain(stmt) => build_alter_domain(&stmt), + ast::Stmt::AlterEventTrigger(stmt) => build_alter_event_trigger(&stmt), + ast::Stmt::AlterExtension(stmt) => build_alter_extension(&stmt), + ast::Stmt::AlterForeignDataWrapper(stmt) => build_alter_foreign_data_wrapper(&stmt), + ast::Stmt::AlterForeignTable(stmt) => build_alter_foreign_table(&stmt), + ast::Stmt::AlterFunction(stmt) => build_alter_function(&stmt), + ast::Stmt::AlterGroup(stmt) => build_alter_group(&stmt), + ast::Stmt::AlterIndex(stmt) => build_alter_index(&stmt), + ast::Stmt::AlterLanguage(stmt) => build_alter_language(&stmt), + ast::Stmt::AlterLargeObject(stmt) => build_alter_large_object(&stmt), + ast::Stmt::AlterMaterializedView(stmt) => build_alter_materialized_view(&stmt), + ast::Stmt::AlterOperator(stmt) => build_alter_operator(&stmt), + ast::Stmt::AlterOperatorClass(stmt) => build_alter_operator_class(&stmt), + ast::Stmt::AlterOperatorFamily(stmt) => build_alter_operator_family(&stmt), + ast::Stmt::AlterPolicy(stmt) => build_alter_policy(&stmt), + ast::Stmt::AlterProcedure(stmt) => build_alter_procedure(&stmt), + ast::Stmt::AlterPropertyGraph(stmt) => build_alter_property_graph(&stmt), + ast::Stmt::AlterRole(stmt) => build_alter_role(&stmt), + ast::Stmt::AlterRoutine(stmt) => build_alter_routine(&stmt), + ast::Stmt::AlterRule(stmt) => build_alter_rule(&stmt), + ast::Stmt::AlterSchema(stmt) => build_alter_schema(&stmt), + ast::Stmt::AlterSequence(stmt) => build_alter_sequence(&stmt), + ast::Stmt::AlterServer(stmt) => build_alter_server(&stmt), + ast::Stmt::AlterStatistics(stmt) => build_alter_statistics(&stmt), + ast::Stmt::AlterSystem(stmt) => build_alter_system(&stmt), + ast::Stmt::AlterTable(stmt) => build_alter_table(&stmt), + ast::Stmt::AlterTablespace(stmt) => build_alter_tablespace(&stmt), + ast::Stmt::AlterTextSearchConfiguration(stmt) => { + build_alter_text_search_configuration(&stmt) + } + ast::Stmt::AlterTextSearchDictionary(stmt) => build_alter_text_search_dictionary(&stmt), + ast::Stmt::AlterTextSearchParser(stmt) => build_alter_text_search_parser(&stmt), + ast::Stmt::AlterTextSearchTemplate(stmt) => build_alter_text_search_template(&stmt), + ast::Stmt::AlterTrigger(stmt) => build_alter_trigger(&stmt), + ast::Stmt::AlterType(stmt) => build_alter_type(&stmt), + ast::Stmt::AlterUser(stmt) => build_alter_user(&stmt), + ast::Stmt::AlterUserMapping(stmt) => build_alter_user_mapping(&stmt), + ast::Stmt::AlterView(stmt) => build_alter_view(&stmt), ast::Stmt::Analyze(stmt) => build_analyze(&stmt), ast::Stmt::Call(stmt) => build_call(&stmt), ast::Stmt::Checkpoint(stmt) => build_checkpoint(&stmt), ast::Stmt::Close(stmt) => build_close(&stmt), ast::Stmt::Cluster(stmt) => build_cluster(&stmt), - ast::Stmt::CommentOn(_) => todo!(), + ast::Stmt::CommentOn(stmt) => build_comment_on(&stmt), ast::Stmt::Copy(stmt) => build_copy(&stmt), - ast::Stmt::CreateAccessMethod(_) => todo!(), - ast::Stmt::CreateAggregate(_) => todo!(), - ast::Stmt::CreateCast(_) => todo!(), - ast::Stmt::CreateCollation(_) => todo!(), - ast::Stmt::CreateConversion(_) => todo!(), - ast::Stmt::CreateDatabase(_) => todo!(), - ast::Stmt::CreateDomain(_) => todo!(), - ast::Stmt::CreateEventTrigger(_) => todo!(), - ast::Stmt::CreateExtension(_) => todo!(), - ast::Stmt::CreateForeignDataWrapper(_) => todo!(), + ast::Stmt::CreateAccessMethod(stmt) => build_create_access_method(&stmt), + ast::Stmt::CreateAggregate(stmt) => build_create_aggregate(&stmt), + ast::Stmt::CreateCast(stmt) => build_create_cast(&stmt), + ast::Stmt::CreateCollation(stmt) => build_create_collation(&stmt), + ast::Stmt::CreateConversion(stmt) => build_create_conversion(&stmt), + ast::Stmt::CreateDatabase(stmt) => build_create_database(&stmt), + ast::Stmt::CreateDomain(stmt) => build_create_domain(&stmt), + ast::Stmt::CreateEventTrigger(stmt) => build_create_event_trigger(&stmt), + ast::Stmt::CreateExtension(stmt) => build_create_extension(&stmt), + ast::Stmt::CreateForeignDataWrapper(stmt) => build_create_foreign_data_wrapper(&stmt), ast::Stmt::CreateForeignTable(stmt) => build_create_foreign_table(&stmt), - ast::Stmt::CreateGroup(_) => todo!(), - ast::Stmt::CreateLanguage(_) => todo!(), - ast::Stmt::CreateMaterializedView(_) => todo!(), - ast::Stmt::CreateOperator(_) => todo!(), - ast::Stmt::CreateOperatorClass(_) => todo!(), - ast::Stmt::CreateOperatorFamily(_) => todo!(), - ast::Stmt::CreatePolicy(_) => todo!(), - ast::Stmt::CreateProcedure(_) => todo!(), - ast::Stmt::CreatePropertyGraph(_) => todo!(), - ast::Stmt::CreateRole(_) => todo!(), - ast::Stmt::CreateRule(_) => todo!(), - ast::Stmt::CreateSchema(_) => todo!(), - ast::Stmt::CreateSequence(_) => todo!(), - ast::Stmt::CreateServer(_) => todo!(), - ast::Stmt::CreateStatistics(_) => todo!(), - ast::Stmt::CreateTablespace(_) => todo!(), - ast::Stmt::CreateTextSearchConfiguration(_) => todo!(), - ast::Stmt::CreateTextSearchDictionary(_) => todo!(), - ast::Stmt::CreateTextSearchParser(_) => todo!(), - ast::Stmt::CreateTextSearchTemplate(_) => todo!(), + ast::Stmt::CreateGroup(stmt) => build_create_group(&stmt), + ast::Stmt::CreateLanguage(stmt) => build_create_language(&stmt), + ast::Stmt::CreateMaterializedView(stmt) => build_create_materialized_view(&stmt), + ast::Stmt::CreateOperator(stmt) => build_create_operator(&stmt), + ast::Stmt::CreateOperatorClass(stmt) => build_create_operator_class(&stmt), + ast::Stmt::CreateOperatorFamily(stmt) => build_create_operator_family(&stmt), + ast::Stmt::CreatePolicy(stmt) => build_create_policy(&stmt), + ast::Stmt::CreateProcedure(stmt) => build_create_procedure(&stmt), + ast::Stmt::CreatePropertyGraph(stmt) => build_create_property_graph(&stmt), + ast::Stmt::CreateRole(stmt) => build_create_role(&stmt), + ast::Stmt::CreateRule(stmt) => build_create_rule(&stmt), + ast::Stmt::CreateSchema(stmt) => build_create_schema(&stmt), + ast::Stmt::CreateSequence(stmt) => build_create_sequence(&stmt), + ast::Stmt::CreateServer(stmt) => build_create_server(&stmt), + ast::Stmt::CreateStatistics(stmt) => build_create_statistics(&stmt), + ast::Stmt::CreateTablespace(stmt) => build_create_tablespace(&stmt), + ast::Stmt::CreateTextSearchConfiguration(stmt) => { + build_create_text_search_configuration(&stmt) + } + ast::Stmt::CreateTextSearchDictionary(stmt) => build_create_text_search_dictionary(&stmt), + ast::Stmt::CreateTextSearchParser(stmt) => build_create_text_search_parser(&stmt), + ast::Stmt::CreateTextSearchTemplate(stmt) => build_create_text_search_template(&stmt), ast::Stmt::CreateTransform(stmt) => build_create_transform(&stmt), ast::Stmt::CreateTrigger(stmt) => build_create_trigger(&stmt), - ast::Stmt::CreateType(_) => todo!(), - ast::Stmt::CreateUser(_) => todo!(), - ast::Stmt::CreateUserMapping(_) => todo!(), + ast::Stmt::CreateType(stmt) => build_create_type(&stmt), + ast::Stmt::CreateUser(stmt) => build_create_user(&stmt), + ast::Stmt::CreateUserMapping(stmt) => build_create_user_mapping(&stmt), ast::Stmt::Deallocate(stmt) => build_deallocate(&stmt), ast::Stmt::Declare(stmt) => build_declare(&stmt), ast::Stmt::Discard(stmt) => build_discard(&stmt), ast::Stmt::Do(stmt) => build_do(&stmt), - ast::Stmt::DropAccessMethod(_) => todo!(), - ast::Stmt::DropAggregate(_) => todo!(), - ast::Stmt::DropCast(_) => todo!(), - ast::Stmt::DropCollation(_) => todo!(), - ast::Stmt::DropConversion(_) => todo!(), - ast::Stmt::DropDatabase(_) => todo!(), - ast::Stmt::DropDomain(_) => todo!(), - ast::Stmt::DropEventTrigger(_) => todo!(), - ast::Stmt::DropExtension(_) => todo!(), - ast::Stmt::DropForeignDataWrapper(_) => todo!(), - ast::Stmt::DropForeignTable(_) => todo!(), - ast::Stmt::DropFunction(_) => todo!(), - ast::Stmt::DropGroup(_) => todo!(), - ast::Stmt::DropIndex(_) => todo!(), - ast::Stmt::DropLanguage(_) => todo!(), - ast::Stmt::DropMaterializedView(_) => todo!(), - ast::Stmt::DropOperator(_) => todo!(), - ast::Stmt::DropOperatorClass(_) => todo!(), - ast::Stmt::DropOperatorFamily(_) => todo!(), - ast::Stmt::DropOwned(_) => todo!(), - ast::Stmt::DropPolicy(_) => todo!(), - ast::Stmt::DropProcedure(_) => todo!(), - ast::Stmt::DropPropertyGraph(_) => todo!(), - ast::Stmt::DropRole(_) => todo!(), - ast::Stmt::DropRoutine(_) => todo!(), - ast::Stmt::DropRule(_) => todo!(), - ast::Stmt::DropSchema(_) => todo!(), - ast::Stmt::DropSequence(_) => todo!(), - ast::Stmt::DropServer(_) => todo!(), - ast::Stmt::DropStatistics(_) => todo!(), - ast::Stmt::DropTable(_) => todo!(), - ast::Stmt::DropTablespace(_) => todo!(), - ast::Stmt::DropTextSearchConfig(_) => todo!(), - ast::Stmt::DropTextSearchDict(_) => todo!(), - ast::Stmt::DropTextSearchParser(_) => todo!(), - ast::Stmt::DropTextSearchTemplate(_) => todo!(), - ast::Stmt::DropTransform(_) => todo!(), - ast::Stmt::DropTrigger(_) => todo!(), - ast::Stmt::DropType(_) => todo!(), - ast::Stmt::DropUser(_) => todo!(), - ast::Stmt::DropUserMapping(_) => todo!(), - ast::Stmt::DropView(_) => todo!(), + ast::Stmt::DropAccessMethod(stmt) => build_drop_access_method(&stmt), + ast::Stmt::DropAggregate(stmt) => build_drop_aggregate(&stmt), + ast::Stmt::DropCast(stmt) => build_drop_cast(&stmt), + ast::Stmt::DropCollation(stmt) => build_drop_collation(&stmt), + ast::Stmt::DropConversion(stmt) => build_drop_conversion(&stmt), + ast::Stmt::DropDatabase(stmt) => build_drop_database(&stmt), + ast::Stmt::DropDomain(stmt) => build_drop_domain(&stmt), + ast::Stmt::DropEventTrigger(stmt) => build_drop_event_trigger(&stmt), + ast::Stmt::DropExtension(stmt) => build_drop_extension(&stmt), + ast::Stmt::DropForeignDataWrapper(stmt) => build_drop_foreign_data_wrapper(&stmt), + ast::Stmt::DropForeignTable(stmt) => build_drop_foreign_table(&stmt), + ast::Stmt::DropFunction(stmt) => build_drop_function(&stmt), + ast::Stmt::DropGroup(stmt) => build_drop_group(&stmt), + ast::Stmt::DropIndex(stmt) => build_drop_index(&stmt), + ast::Stmt::DropLanguage(stmt) => build_drop_language(&stmt), + ast::Stmt::DropMaterializedView(stmt) => build_drop_materialized_view(&stmt), + ast::Stmt::DropOperator(stmt) => build_drop_operator(&stmt), + ast::Stmt::DropOperatorClass(stmt) => build_drop_operator_class(&stmt), + ast::Stmt::DropOperatorFamily(stmt) => build_drop_operator_family(&stmt), + ast::Stmt::DropOwned(stmt) => build_drop_owned(&stmt), + ast::Stmt::DropPolicy(stmt) => build_drop_policy(&stmt), + ast::Stmt::DropProcedure(stmt) => build_drop_procedure(&stmt), + ast::Stmt::DropPropertyGraph(stmt) => build_drop_property_graph(&stmt), + ast::Stmt::DropRole(stmt) => build_drop_role(&stmt), + ast::Stmt::DropRoutine(stmt) => build_drop_routine(&stmt), + ast::Stmt::DropRule(stmt) => build_drop_rule(&stmt), + ast::Stmt::DropSchema(stmt) => build_drop_schema(&stmt), + ast::Stmt::DropSequence(stmt) => build_drop_sequence(&stmt), + ast::Stmt::DropServer(stmt) => build_drop_server(&stmt), + ast::Stmt::DropStatistics(stmt) => build_drop_statistics(&stmt), + ast::Stmt::DropTable(stmt) => build_drop_table(&stmt), + ast::Stmt::DropTablespace(stmt) => build_drop_tablespace(&stmt), + ast::Stmt::DropTextSearchConfig(stmt) => build_drop_text_search_config(&stmt), + ast::Stmt::DropTextSearchDict(stmt) => build_drop_text_search_dict(&stmt), + ast::Stmt::DropTextSearchParser(stmt) => build_drop_text_search_parser(&stmt), + ast::Stmt::DropTextSearchTemplate(stmt) => build_drop_text_search_template(&stmt), + ast::Stmt::DropTransform(stmt) => build_drop_transform(&stmt), + ast::Stmt::DropTrigger(stmt) => build_drop_trigger(&stmt), + ast::Stmt::DropType(stmt) => build_drop_type(&stmt), + ast::Stmt::DropUser(stmt) => build_drop_user(&stmt), + ast::Stmt::DropUserMapping(stmt) => build_drop_user_mapping(&stmt), + ast::Stmt::DropView(stmt) => build_drop_view(&stmt), ast::Stmt::Execute(stmt) => build_execute(stmt), ast::Stmt::Explain(stmt) => build_explain(&stmt), ast::Stmt::Fetch(stmt) => build_fetch(&stmt), @@ -3508,951 +3518,1242 @@ fn build_stmt<'a>(stmt: ast::Stmt) -> Doc<'a> { } } -fn build_return_function_option<'a>(option: ast::ReturnFuncOption) -> Doc<'a> { - let mut doc = Doc::text("return"); - if let Some(expr) = option.expr() { +fn build_alter_view<'a>(stmt: &ast::AlterView) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.alter_token(), "alter"), (stmt.view_token(), "view")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc.append(build_semicolon(option.semicolon_token())) -} - -fn build_literal_function_option<'a>( - keyword: &'static str, - literal: Option, -) -> Doc<'a> { - let mut doc = Doc::text(keyword); - if let Some(literal) = literal { + if let Some(view) = stmt.view_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); + .append(build_extension_path_value(&view)); } - doc + if let Some(action) = stmt.action() { + let syntax = action.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_alter_view_action(action)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_as_function_option<'a>(option: ast::AsFuncOption) -> Doc<'a> { - let mut doc = Doc::text("as"); - if let Some(target) = option.as_func_target() { - match target { - ast::AsFuncTarget::AsDefinition(definition) => { - if let Some(literal) = definition.literal() { - doc = doc - .append(Doc::space()) - .append(leading_comments(definition.syntax())) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); - } +fn build_alter_view_action<'a>(action: ast::AlterViewAction) -> Doc<'a> { + match action { + ast::AlterViewAction::AlterViewColumn(action) => { + let mut doc = build_keyword_tokens([ + (action.alter_token(), "alter"), + (action.column_token(), "column"), + ]); + doc = append_commented_name(doc, action.name()); + if let Some(column_action) = action.alter_view_column_action() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(column_action.syntax())) + .append(match column_action { + ast::AlterViewColumnAction::DropDefault(action) => build_keyword_tokens([ + (action.drop_token(), "drop"), + (action.default_token(), "default"), + ]), + ast::AlterViewColumnAction::SetDefault(action) => { + let mut doc = build_keyword_tokens([ + (action.set_token(), "set"), + (action.default_token(), "default"), + ]); + if let Some(expr) = action.expr() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + doc.group() + } + }); } - ast::AsFuncTarget::AsObjFile(obj_file) => { + doc.group() + } + ast::AlterViewAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterViewAction::RenameColumn(action) => { + let mut doc = build_keyword_tokens([ + (action.rename_token(), "rename"), + (action.column_token(), "column"), + ]); + doc = append_commented_name(doc, action.column_name_ref()); + if let Some(to) = action.to_token() { doc = doc .append(Doc::space()) - .append(leading_comments(obj_file.syntax())); - if let Some(literal) = obj_file.obj_file() { - doc = doc - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); - } - if let Some(comma) = obj_file.comma_token() { - doc = doc.append(comments_before(comma)).append(Doc::text(",")); - } - if let Some(literal) = obj_file.link_symbol() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); - } - doc = doc.group(); + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + append_commented_name(doc, action.column_name()) + } + ast::AlterViewAction::ResetOptions(action) => { + let mut doc = Doc::text("reset"); + if let Some(options) = action.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(options.syntax())) + .append(build_attribute_list(&options)); } + doc + } + ast::AlterViewAction::SetOptions(action) => build_set_options(&action), + ast::AlterViewAction::SetSchema(action) => build_set_schema(&action), + ast::AlterViewAction::ViewRenameTo(action) => { + let target = action.view().map(|view| { + leading_comments(view.syntax()).append( + view.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) } } - doc } -fn build_explain<'a>(explain: &ast::Explain) -> Doc<'a> { - let mut doc = Doc::text("explain"); - if let Some(mode) = explain.explain_mode() { - let mode_syntax = mode.syntax().clone(); - let parenthesized = matches!(&mode, ast::ExplainMode::ExplainOptionList(_)); - let mode_doc = match mode { - ast::ExplainMode::ExplainAnalyze(analyze) => { - let mut doc = if analyze.analyse_token().is_some() { - Doc::text("analyse") - } else { - Doc::text("analyze") - }; - if let Some(verbose) = analyze.explain_verbose() { - doc = doc - .append(Doc::space()) - .append(leading_comments(verbose.syntax())) - .append(build_keyword_node(verbose.syntax())); - } - doc - } - ast::ExplainMode::ExplainVerbose(verbose) => build_keyword_node(verbose.syntax()), - ast::ExplainMode::ExplainOptionList(options) => build_explain_option_list(options), - }; - let mode_doc = leading_comments(&mode_syntax).append(mode_doc); - doc = if parenthesized { - doc.append(Doc::space()).append(mode_doc) - } else { - doc.append(Doc::line_or_space().append(mode_doc).nest(2)) - }; +fn build_create_access_method<'a>(stmt: &ast::CreateAccessMethod) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.access_token(), "access"), + (stmt.method_token(), "method"), + ]); + if let Some(method) = stmt.access_method() { + doc = doc + .append(Doc::space()) + .append(leading_comments(method.syntax())) + .append(build_name(method.syntax())); } - if let Some(stmt) = explain.explain_stmt() { + if let Some(type_token) = stmt.type_token() { + let mut clause = leading_comments_token(&type_token).append(Doc::text("type")); + if let Some(kind) = stmt.access_method_type() { + clause = clause + .append(Doc::space()) + .append(leading_comments(kind.syntax())) + .append(build_keyword_node(kind.syntax())); + } + doc = doc.append(Doc::hard_line().append(clause).nest(2)); + } + if let Some(handler) = stmt.handler_clause() { + let mut clause = build_keyword_tokens([(handler.handler_token(), "handler")]); + if let Some(function) = handler.function_name_ref() { + clause = clause + .append(Doc::space()) + .append(build_extension_path_value(&function)); + } doc = doc.append( Doc::hard_line() - .append(leading_comments(stmt.syntax())) - .append(build_explain_stmt(stmt)) + .append(leading_comments(handler.syntax())) + .append(clause) .nest(2), ); } - doc.group() - .append(build_semicolon(explain.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_explain_option_list<'a>(list: ast::ExplainOptionList) -> Doc<'a> { - let mut body = build_comma_separated_docs(list.explain_options().map(|option| { - let syntax = option.syntax().clone(); - let mut doc = option - .explain_option_name() - .map(|name| build_keyword_node(name.syntax())) - .unwrap_or_else(Doc::nil); - if let Some(value) = option.explain_option_value() { - let value_doc = value - .expr() - .map(build_expr) - .unwrap_or_else(|| build_keyword_node(value.syntax())); - doc = doc - .append(Doc::space()) - .append(leading_comments(value.syntax())) - .append(value_doc); +fn build_aggregate_param_list<'a>(params: ast::ParamList) -> Doc<'a> { + let doc = params + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let mut body = if let Some(star) = params.star_token() { + leading_comments_token(&star).append(Doc::text("*")) + } else { + let body = build_function_params(params.params()); + let has_params = body.is_some(); + let mut body = body.unwrap_or_else(Doc::nil); + if let Some(order_by) = params.aggregate_order_by() { + if has_params { + body = body.append(Doc::line_or_space()); + } + body = body + .append(leading_comments(order_by.syntax())) + .append(build_aggregate_order_by(order_by)); } - (leading_comments(&syntax).append(doc), syntax) - })) - .unwrap_or_else(Doc::nil); - if let Some(r_paren) = list.r_paren_token() { + body + }; + if let Some(r_paren) = params.r_paren_token() { body = body.append(comments_before(r_paren)); } - list.l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")) - .append(Doc::hard_line().append(body.group()).nest(2)) - .append(Doc::hard_line()) - .append(Doc::text(")")) + doc.append(wrap_body(body)).append(Doc::text(")")).group() } -fn build_create_materialized_view<'a>(view: &ast::CreateMaterializedView) -> Doc<'a> { +fn build_aggregate_order_by<'a>(order_by: ast::AggregateOrderBy) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (order_by.order_token(), "order"), + (order_by.by_token(), "by"), + ]); + if let Some(params) = build_function_params(order_by.params()) { + doc = doc.append(Doc::space()).append(params); + } + doc +} + +fn build_create_aggregate<'a>(stmt: &ast::CreateAggregate) -> Doc<'a> { let mut doc = Doc::text("create"); - for (token, keyword) in [ - (view.materialized_token(), "materialized"), - (view.view_token(), "view"), - ] { - if let Some(token) = token { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)); - } + if let Some(or_replace) = stmt.or_replace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(or_replace.syntax())) + .append(build_keyword_node(or_replace.syntax())); } - if let Some(if_not_exists) = view.if_not_exists() { + if let Some(token) = stmt.aggregate_token() { doc = doc .append(Doc::space()) - .append(leading_comments(if_not_exists.syntax())) - .append(build_keyword_node(if_not_exists.syntax())); + .append(leading_comments_token(&token)) + .append(Doc::text("aggregate")); } - if let Some(name) = view.view() { + if let Some(name) = stmt.aggregate_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); if let Some(path) = name.path() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_path(&path)); + doc = doc.append(build_path(&path)); } } - if let Some(columns) = view.column_list() { + if let Some(params) = stmt.param_list() { + doc = doc + .append(leading_comments(params.syntax())) + .append(build_aggregate_param_list(params)); + } + if let Some(attributes) = stmt.attribute_list() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_cte_column_list(columns)); + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); } - if let Some(using) = view.using_method() { - let mut option = Doc::text("using"); - if let Some(method) = using.access_method_ref() { - option = option - .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(build_name(method.syntax())); - } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_cast<'a>(stmt: &ast::CreateCast) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.create_token(), "create"), (stmt.cast_token(), "cast")]); + if let Some(sig) = stmt.cast_sig() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sig.syntax())) + .append(build_cast_sig(sig)); + } + if let Some(method) = stmt.cast_method() { + let method_syntax = method.syntax().clone(); + let method_doc = match method { + ast::CastMethod::WithFunction(method) => { + let mut doc = build_keyword_tokens([ + (method.with_token(), "with"), + (method.function_token(), "function"), + ]); + if let Some(function) = method.function_sig() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(function.syntax())) + .append(build_function_sig(function)) + .nest(2); + } + doc.group() + } + ast::CastMethod::WithInout(method) => build_keyword_tokens([ + (method.with_token(), "with"), + (method.inout_token(), "inout"), + ]), + ast::CastMethod::WithoutFunction(method) => build_keyword_tokens([ + (method.without_token(), "without"), + (method.function_token(), "function"), + ]), + }; doc = doc.append( Doc::line_or_space() - .append(leading_comments(using.syntax())) - .append(option) + .append(leading_comments(&method_syntax)) + .append(method_doc) .nest(2), ); } - if let Some(params) = view.with_params() { + if let Some(context) = stmt.cast_context() { + let context_syntax = context.syntax().clone(); + let context_doc = match context { + ast::CastContext::AsAssignment(context) => build_keyword_tokens([ + (context.as_token(), "as"), + (context.assignment_token(), "assignment"), + ]), + ast::CastContext::AsImplicit(context) => build_keyword_tokens([ + (context.as_token(), "as"), + (context.implicit_token(), "implicit"), + ]), + }; doc = doc.append( Doc::line_or_space() - .append(leading_comments(params.syntax())) - .append(build_with_params(params)) + .append(leading_comments(&context_syntax)) + .append(context_doc) .nest(2), ); } - if let Some(tablespace) = view.tablespace_clause() { - let mut option = Doc::text("tablespace"); - if let Some(name) = tablespace.tablespace_ref() { - option = option - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_collation<'a>(stmt: &ast::CreateCollation) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.collation_token(), "collation"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + if let Some(collation) = stmt.collation() { + let name = leading_comments(collation.syntax()).append( + collation + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ); + doc = doc.append(Doc::space()).append(name); + } + if let Some(definition) = stmt.definition() { + let definition_syntax = definition.syntax().clone(); + match definition { + ast::CreateCollationDefinition::AttributeList(attributes) => { + doc = doc + .append(Doc::space()) + .append(leading_comments(&definition_syntax)) + .append(build_multiline_attribute_list(&attributes)); + } + ast::CreateCollationDefinition::CollationFrom(from) => { + let mut from_doc = build_keyword_tokens([(from.from_token(), "from")]); + if let Some(collation) = from.collation_ref() { + from_doc = from_doc + .append(Doc::line_or_space()) + .append(leading_comments(collation.syntax())); + if let Some(path) = collation.path_ref() { + from_doc = from_doc.append(build_path_ref(&path)); + } + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&definition_syntax)) + .append(from_doc.group()) + .nest(2), + ); + } + } + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_conversion<'a>(stmt: &ast::CreateConversion) -> Doc<'a> { + let mut doc = Doc::text("create"); + if let Some(token) = stmt.default_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("default")); + } + if let Some(token) = stmt.conversion_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("conversion")); + } + if let Some(conversion) = stmt.conversion() { + let name = leading_comments(conversion.syntax()).append( + conversion + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ); + doc = doc.append(Doc::space()).append(name); + } + if let Some(token) = stmt.for_token() { + let mut clause = leading_comments_token(&token).append(Doc::text("for")); + if let Some(encoding) = stmt.for_() { + clause = clause + .append(Doc::space()) + .append(leading_comments(encoding.syntax())) + .append(build_literal(encoding)); + } + doc = doc.append(Doc::line_or_space().append(clause).nest(2)); + } + if let Some(token) = stmt.to_token() { + let mut clause = leading_comments_token(&token).append(Doc::text("to")); + if let Some(encoding) = stmt.to() { + clause = clause + .append(Doc::space()) + .append(leading_comments(encoding.syntax())) + .append(build_literal(encoding)); + } + doc = doc.append(Doc::line_or_space().append(clause).nest(2)); + } + if let Some(token) = stmt.from_token() { + let mut clause = leading_comments_token(&token).append(Doc::text("from")); + if let Some(function) = stmt.function_name_ref() { + clause = clause.append( + Doc::line_or_space() + .append(build_extension_path_value(&function)) + .nest(2), + ); } + doc = doc.append(Doc::line_or_space().append(clause.group()).nest(2)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_database<'a>(stmt: &ast::CreateDatabase) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.database_token(), "database"), + ]); + if let Some(database) = stmt.database() { + let name = leading_comments(database.syntax()).append(build_name(database.syntax())); + doc = doc.append(Doc::space()).append(name); + } + if let Some(options) = stmt.database_option_list() + && (options.with_token().is_some() || options.database_options().next().is_some()) + { doc = doc.append( Doc::line_or_space() - .append(leading_comments(tablespace.syntax())) - .append(option) + .append(leading_comments(options.syntax())) + .append(build_database_option_list(options)) .nest(2), ); } - if let Some(as_token) = view.as_token() { - doc = doc - .append(Doc::hard_line()) - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_domain<'a>(stmt: &ast::CreateDomain) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.domain_token(), "domain"), + ]); + if let Some(domain) = stmt.domain() { + let name = leading_comments(domain.syntax()).append( + domain + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ); + doc = doc.append(Doc::space()).append(name); } - if let Some(query) = view.query() { + if let Some(as_token) = stmt.as_token() { doc = doc.append( - Doc::hard_line() - .append(leading_comments(query.syntax())) - .append(build_select_variant(query)) + Doc::line_or_space() + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")) .nest(2), ); } - if let Some(data) = view.data_option() { + if let Some(ty) = stmt.ty() { + let separator = if stmt.as_token().is_some() { + Doc::space() + } else { + Doc::line_or_space() + }; doc = doc.append( - Doc::hard_line() - .append(leading_comments(data.syntax())) - .append(build_keyword_node(data.syntax())) + separator + .append(leading_comments(ty.syntax())) + .append(build_type(ty)) .nest(2), ); } - doc.group().append(build_semicolon(view.semicolon_token())) -} - -fn build_explain_stmt<'a>(stmt: ast::ExplainStmt) -> Doc<'a> { - match stmt { - ast::ExplainStmt::CompoundSelect(stmt) => build_compound_select(&stmt), - ast::ExplainStmt::CreateTableAs(stmt) => build_create_table_as(&stmt), - ast::ExplainStmt::Declare(stmt) => build_declare(&stmt), - ast::ExplainStmt::Delete(stmt) => build_delete(&stmt), - ast::ExplainStmt::Execute(stmt) => build_execute(stmt), - ast::ExplainStmt::Insert(stmt) => build_insert(&stmt), - ast::ExplainStmt::Merge(stmt) => build_merge(&stmt), - ast::ExplainStmt::ParenSelect(stmt) => build_paren_select(stmt), - ast::ExplainStmt::Select(stmt) => build_select_doc(&stmt), - ast::ExplainStmt::SelectInto(stmt) => build_select_into(&stmt), - ast::ExplainStmt::Table(stmt) => build_table(&stmt), - ast::ExplainStmt::Update(stmt) => build_update(&stmt), - ast::ExplainStmt::Values(stmt) => build_values(&stmt), - ast::ExplainStmt::CreateMaterializedView(stmt) => build_create_materialized_view(&stmt), + if let Some(collate) = stmt.collate() { + let syntax = collate.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_collate_expr(collate)) + .nest(2), + ); } + for constraint in stmt.constraints() { + let syntax = constraint.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_domain_constraint(constraint)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_role_ref_list<'a>(list: ast::RoleRefList) -> Doc<'a> { - build_comma_separated_docs(list.role_refs().map(|role| { - let syntax = role.syntax().clone(); - ( - leading_comments(&syntax).append(build_role_ref(&role)), - syntax, - ) - })) - .unwrap_or_else(Doc::nil) -} - -fn build_keyword_tokens<'a, const N: usize>( - tokens: [(Option, &'static str); N], -) -> Doc<'a> { - let mut doc = Doc::nil(); - let mut has_keyword = false; - for (token, keyword) in tokens { - if let Some(token) = token { - if has_keyword { - doc = doc.append(Doc::space()); - } - has_keyword = true; - doc = doc +fn build_create_event_trigger<'a>(stmt: &ast::CreateEventTrigger) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.event_token(), "event"), + (stmt.trigger_token(), "trigger"), + ]); + if let Some(name) = stmt.event_trigger() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(on_token) = stmt.on_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&on_token)) + .append(Doc::text("on")) + .nest(2), + ); + } + if let Some(event) = stmt.event() { + doc = doc + .append(Doc::space()) + .append(leading_comments(event.syntax())) + .append(build_name(event.syntax())); + } + if let Some(when_clause) = stmt.event_trigger_when_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(when_clause.syntax())) + .append(build_event_trigger_when_clause(when_clause)) + .nest(2), + ); + } + let mut execute_clause = stmt + .execute_token() + .map(|execute_token| leading_comments_token(&execute_token).append(Doc::text("execute"))); + if let Some(token) = stmt.function_token().or_else(|| stmt.procedure_token()) { + let keyword = if token.kind() == SyntaxKind::FUNCTION_KW { + "function" + } else { + "procedure" + }; + execute_clause = Some( + execute_clause + .unwrap_or_else(Doc::nil) + .append(Doc::space()) .append(leading_comments_token(&token)) - .append(Doc::text(keyword)); - } + .append(Doc::text(keyword)), + ); } - doc + if let Some(call) = stmt.call_expr() { + execute_clause = Some( + execute_clause + .unwrap_or_else(Doc::nil) + .append(Doc::line_or_space()) + .append(leading_comments(call.syntax())) + .append(build_call_expr(call)) + .nest(2) + .group(), + ); + } + if let Some(execute_clause) = execute_clause { + doc = doc.append(Doc::line_or_space().append(execute_clause).nest(2)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_revoke_command<'a>(command: ast::RevokeCommand) -> Doc<'a> { - let mut doc = if let Some(role) = command.role_ref() { - build_role_ref(&role) - } else if command.ident_token().is_some() { - build_name(command.syntax()) - } else { - let tokens = [ - (command.all_token(), "all"), - (command.alter_token(), "alter"), - (command.create_token(), "create"), - (command.delete_token(), "delete"), - (command.execute_token(), "execute"), - (command.insert_token(), "insert"), - (command.references_token(), "references"), - (command.select_token(), "select"), - (command.system_token(), "system"), - (command.temp_token(), "temp"), - (command.temporary_token(), "temporary"), - (command.trigger_token(), "trigger"), - (command.truncate_token(), "truncate"), - (command.update_token(), "update"), - ]; - build_keyword_tokens(tokens) - }; - if let Some(columns) = command.column_ref_list() { +fn build_event_trigger_when_clause<'a>(clause: ast::EventTriggerWhenClause) -> Doc<'a> { + let mut doc = clause + .when_token() + .map(|token| leading_comments_token(&token).append(Doc::text("when"))) + .unwrap_or_else(Doc::nil); + if let Some(filter) = clause.event_trigger_when() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); + .append(leading_comments(filter.syntax())) + .append(build_event_trigger_when(filter)); } - doc.group() -} - -fn build_privileges<'a>(privileges: ast::Privileges) -> Doc<'a> { - match privileges { - ast::Privileges::AllPrivileges(all) => { - let mut doc = build_keyword_tokens([ - (all.all_token(), "all"), - (all.privileges_token(), "privileges"), - ]); - if let Some(columns) = all.column_ref_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); - } - doc + for and_filter in clause.event_trigger_when_ands() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(and_filter.syntax())); + if and_filter.and_token().is_some() { + doc = doc.append(Doc::text("and")); } - ast::Privileges::RevokeCommandList(commands) => { - build_comma_separated_docs(commands.revoke_commands().map(|command| { - let syntax = command.syntax().clone(); - ( - leading_comments(&syntax).append(build_revoke_command(command)), - syntax, - ) - })) - .unwrap_or_else(Doc::nil) + if let Some(filter) = and_filter.event_trigger_when() { + doc = doc + .append(Doc::space()) + .append(leading_comments(filter.syntax())) + .append(build_event_trigger_when(filter)); } } + doc.group() } -fn build_path_items<'a>(items: Vec<(SyntaxNode, ast::PathRef)>) -> Doc<'a> { - build_comma_separated_docs(items.into_iter().map(|(syntax, path)| { +fn build_event_trigger_when<'a>(filter: ast::EventTriggerWhen) -> Doc<'a> { + let mut doc = filter + .var() + .map(|var| build_name(var.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(in_token) = filter.in_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&in_token)) + .append(Doc::text("in")); + } + if let Some(l_paren) = filter.l_paren_token() { + doc = doc.append(comments_before(l_paren)); + } + let items = filter.literals().map(|literal| { + let syntax = literal.syntax().clone(); ( - leading_comments(&syntax).append(build_path_ref(&path)), + leading_comments(&syntax).append(build_literal(literal)), syntax, ) - })) - .unwrap_or_else(Doc::nil) -} - -fn build_name_items<'a>(items: Vec) -> Doc<'a> { - build_comma_separated_docs(items.into_iter().map(|syntax| { - let doc = leading_comments(&syntax).append(build_name(&syntax)); - (doc, syntax) - })) - .unwrap_or_else(Doc::nil) -} - -fn append_privilege_items<'a>(prefix: Doc<'a>, items: Doc<'a>) -> Doc<'a> { - prefix - .append(Doc::line_or_space().append(items).nest(2)) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = filter.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(Doc::space()) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) .group() } -fn build_function_sig<'a>(sig: ast::FunctionSig) -> Doc<'a> { - let mut doc = sig - .function_name_ref() - .and_then(|name| name.path_ref()) - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - if let Some(params) = sig.param_list() { +fn build_create_extension<'a>(stmt: &ast::CreateExtension) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.extension_token(), "extension"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { doc = doc - .append(leading_comments(params.syntax())) - .append(build_function_param_list(params)); + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); } - doc + if let Some(extension) = stmt.extension() { + doc = doc + .append(Doc::space()) + .append(leading_comments(extension.syntax())) + .append(build_name(extension.syntax())); + } + if let Some(with_token) = stmt.with_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&with_token)) + .append(Doc::text("with")) + .nest(2), + ); + } + if let Some(schema) = stmt.extension_schema() { + let mut clause = schema + .schema_token() + .map(|token| leading_comments_token(&token).append(Doc::text("schema"))) + .unwrap_or_else(Doc::nil); + if let Some(name) = schema.schema_ref() { + clause = clause + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(schema.syntax())) + .append(clause) + .nest(2), + ); + } + if let Some(version) = stmt.extension_version_clause() { + let mut clause = version + .version_token() + .map(|token| leading_comments_token(&token).append(Doc::text("version"))) + .unwrap_or_else(Doc::nil); + if let Some(literal) = version.literal() { + clause = clause + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } else if let Some(value) = version.extension_version() { + clause = clause + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_name(value.syntax())); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(version.syntax())) + .append(clause) + .nest(2), + ); + } + if let Some(cascade_token) = stmt.cascade_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&cascade_token)) + .append(Doc::text("cascade")) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_procedure_sig<'a>(sig: ast::ProcedureSig) -> Doc<'a> { - let mut doc = sig - .procedure_name_ref() - .and_then(|name| name.path_ref()) - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - if let Some(params) = sig.param_list() { +fn build_create_foreign_data_wrapper<'a>(stmt: &ast::CreateForeignDataWrapper) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.foreign_token(), "foreign"), + (stmt.data_token(), "data"), + (stmt.wrapper_token(), "wrapper"), + ]); + if let Some(name) = stmt.foreign_data_wrapper() { doc = doc - .append(leading_comments(params.syntax())) - .append(build_function_param_list(params)); + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); } - doc + if let Some(options) = stmt.fdw_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_create_fdw_option_list(options)) + .nest(2), + ); + } + if let Some(options) = stmt.alter_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_alter_option_list(&options)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_routine_sig<'a>(sig: ast::RoutineSig) -> Doc<'a> { - let mut doc = sig - .routine_name_ref() - .and_then(|name| name.path_ref()) - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - if let Some(params) = sig.param_list() { +fn build_create_fdw_option_list<'a>(list: ast::FdwOptionList) -> Doc<'a> { + let options = list + .fdw_options() + .map(|option| leading_comments(option.syntax()).append(build_fdw_option(option))); + Doc::list(Itertools::intersperse(options, Doc::line_or_space()).collect()).group() +} + +fn build_comment_on<'a>(stmt: &ast::CommentOn) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.comment_token(), "comment"), (stmt.on_token(), "on")]); + if let Some(object) = stmt.comment_object() { doc = doc - .append(leading_comments(params.syntax())) - .append(build_function_param_list(params)); + .append(Doc::space()) + .append(leading_comments(object.syntax())) + .append(build_comment_object(object).nest(2)); } - doc + if let Some(token) = stmt.is_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&token)) + .append(Doc::text("is")) + .nest(2), + ); + } + if let Some(literal) = stmt.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } else if let Some(token) = stmt.null_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("null")); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_privilege_objects<'a>(objects: ast::PrivilegeObjects) -> Doc<'a> { - macro_rules! direct_items { - ($node:ident, $method:ident, $tokens:expr) => {{ - let items = - build_name_items($node.$method().map(|item| item.syntax().clone()).collect()); - append_privilege_items(build_keyword_tokens($tokens), items) - }}; - } - match objects { - ast::PrivilegeObjects::PrivilegeAllFunctionsInSchema(node) => direct_items!( - node, - schema_refs, - [ - (node.all_token(), "all"), - (node.functions_token(), "functions"), - (node.in_token(), "in"), - (node.schema_token(), "schema") - ] - ), - ast::PrivilegeObjects::PrivilegeAllProceduresInSchema(node) => direct_items!( - node, - schema_refs, - [ - (node.all_token(), "all"), - (node.procedures_token(), "procedures"), - (node.in_token(), "in"), - (node.schema_token(), "schema") - ] - ), - ast::PrivilegeObjects::PrivilegeAllRoutinesInSchema(node) => direct_items!( - node, - schema_refs, - [ - (node.all_token(), "all"), - (node.routines_token(), "routines"), - (node.in_token(), "in"), - (node.schema_token(), "schema") - ] - ), - ast::PrivilegeObjects::PrivilegeAllSequencesInSchema(node) => direct_items!( - node, - schema_refs, - [ - (node.all_token(), "all"), - (node.sequences_token(), "sequences"), - (node.in_token(), "in"), - (node.schema_token(), "schema") - ] - ), - ast::PrivilegeObjects::PrivilegeAllTablesInSchema(node) => direct_items!( - node, - schema_refs, - [ - (node.all_token(), "all"), - (node.tables_token(), "tables"), - (node.in_token(), "in"), - (node.schema_token(), "schema") - ] - ), - ast::PrivilegeObjects::PrivilegeDatabase(node) => { - direct_items!(node, database_refs, [(node.database_token(), "database")]) +fn build_comment_object<'a>(object: ast::CommentObject) -> Doc<'a> { + match object { + ast::CommentObject::ObjectAccessMethod(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectAccessMethod(node)) } - ast::PrivilegeObjects::PrivilegeDefault(node) => build_path_items( - node.relation_name_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ), - ast::PrivilegeObjects::PrivilegeDomain(node) => { - let items = build_path_items( - node.domain_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ); - append_privilege_items( - build_keyword_tokens([(node.domain_token(), "domain")]), - items, - ) + ast::CommentObject::ObjectAggregate(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectAggregate(node)) } - ast::PrivilegeObjects::PrivilegeForeignDataWrapper(node) => direct_items!( - node, - foreign_data_wrapper_refs, - [ - (node.foreign_token(), "foreign"), - (node.data_token(), "data"), - (node.wrapper_token(), "wrapper") - ] - ), - ast::PrivilegeObjects::PrivilegeForeignServer(node) => direct_items!( - node, - server_refs, - [ - (node.foreign_token(), "foreign"), - (node.server_token(), "server") - ] - ), - ast::PrivilegeObjects::PrivilegeLanguage(node) => { - direct_items!(node, language_refs, [(node.language_token(), "language")]) + ast::CommentObject::ObjectCast(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectCast(node)) } - ast::PrivilegeObjects::PrivilegeParameter(node) => { - let items = - build_comma_separated_docs(node.config_parameter_refs().filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| { - ( - leading_comments(&syntax).append(build_path_ref(&path)), - syntax, - ) - }) - })) - .unwrap_or_else(Doc::nil); - append_privilege_items( - build_keyword_tokens([(node.parameter_token(), "parameter")]), - items, - ) + ast::CommentObject::ObjectCollation(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectCollation(node)) } - ast::PrivilegeObjects::PrivilegePropertyGraph(node) => { - let items = build_path_items( - node.property_graph_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ); - append_privilege_items( - build_keyword_tokens([ - (node.property_token(), "property"), - (node.graph_token(), "graph"), - ]), - items, - ) + ast::CommentObject::ObjectConversion(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectConversion(node)) } - ast::PrivilegeObjects::PrivilegeSchema(node) => { - direct_items!(node, schema_refs, [(node.schema_token(), "schema")]) + ast::CommentObject::ObjectDatabase(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectDatabase(node)) } - ast::PrivilegeObjects::PrivilegeSequence(node) => { - let items = build_path_items( - node.sequence_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ); - append_privilege_items( - build_keyword_tokens([(node.sequence_token(), "sequence")]), - items, - ) + ast::CommentObject::ObjectDomain(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectDomain(node)) } - ast::PrivilegeObjects::PrivilegeTable(node) => { - let items = build_path_items( - node.relation_name_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ); - append_privilege_items(build_keyword_tokens([(node.table_token(), "table")]), items) + ast::CommentObject::ObjectEventTrigger(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectEventTrigger(node)) } - ast::PrivilegeObjects::PrivilegeTablespace(node) => direct_items!( - node, - tablespace_refs, - [(node.tablespace_token(), "tablespace")] + ast::CommentObject::ObjectExtension(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectExtension(node)) + } + ast::CommentObject::ObjectForeignDataWrapper(node) => build_extension_member_object( + ast::ExtensionMemberObject::ObjectForeignDataWrapper(node), ), - ast::PrivilegeObjects::PrivilegeType(node) => { - let items = build_path_items( - node.type_name_refs() - .filter_map(|item| { - let syntax = item.syntax().clone(); - item.path_ref().map(|path| (syntax, path)) - }) - .collect(), - ); - append_privilege_items(build_keyword_tokens([(node.type_token(), "type")]), items) + ast::CommentObject::ObjectForeignTable(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectForeignTable(node)) } - ast::PrivilegeObjects::PrivilegeLargeObject(node) => { - let items = build_comma_separated_docs(node.literals().map(|item| { - let syntax = item.syntax().clone(); - ( - leading_comments(&syntax).append(build_literal(item)), - syntax, - ) - })) - .unwrap_or_else(Doc::nil); - append_privilege_items( - build_keyword_tokens([ - (node.large_token(), "large"), - (node.object_token(), "object"), - ]), - items, - ) + ast::CommentObject::ObjectFunction(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectFunction(node)) } - ast::PrivilegeObjects::PrivilegeFunction(node) => { - let items = node - .function_sig_list() - .and_then(|list| { - build_comma_separated_docs(list.function_sigs().map(|sig| { - let syntax = sig.syntax().clone(); - ( - leading_comments(&syntax).append(build_function_sig(sig)), - syntax, - ) - })) - }) - .unwrap_or_else(Doc::nil); - append_privilege_items( - build_keyword_tokens([(node.function_token(), "function")]), - items, - ) + ast::CommentObject::ObjectIndex(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectIndex(node)) } - ast::PrivilegeObjects::PrivilegeProcedure(node) => { - let items = node - .procedure_sig_list() - .and_then(|list| { - build_comma_separated_docs(list.procedure_sigs().map(|sig| { - let syntax = sig.syntax().clone(); - ( - leading_comments(&syntax).append(build_procedure_sig(sig)), - syntax, - ) - })) - }) - .unwrap_or_else(Doc::nil); - append_privilege_items( - build_keyword_tokens([(node.procedure_token(), "procedure")]), - items, - ) + ast::CommentObject::ObjectLanguage(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectLanguage(node)) } - ast::PrivilegeObjects::PrivilegeRoutine(node) => { - let items = node - .routine_sig_list() - .and_then(|list| { - build_comma_separated_docs(list.routine_sigs().map(|sig| { - let syntax = sig.syntax().clone(); - ( - leading_comments(&syntax).append(build_routine_sig(sig)), - syntax, - ) - })) - }) - .unwrap_or_else(Doc::nil); - append_privilege_items( - build_keyword_tokens([(node.routine_token(), "routine")]), - items, - ) + ast::CommentObject::ObjectMaterializedView(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectMaterializedView(node)) + } + ast::CommentObject::ObjectOperator(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectOperator(node)) + } + ast::CommentObject::ObjectOperatorClass(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectOperatorClass(node)) + } + ast::CommentObject::ObjectOperatorFamily(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectOperatorFamily(node)) + } + ast::CommentObject::ObjectProcedure(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectProcedure(node)) + } + ast::CommentObject::ObjectPublication(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectPublication(node)) + } + ast::CommentObject::ObjectRole(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectRole(node)) + } + ast::CommentObject::ObjectRoutine(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectRoutine(node)) + } + ast::CommentObject::ObjectSchema(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectSchema(node)) + } + ast::CommentObject::ObjectSequence(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectSequence(node)) + } + ast::CommentObject::ObjectServer(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectServer(node)) + } + ast::CommentObject::ObjectStatistics(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectStatistics(node)) + } + ast::CommentObject::ObjectSubscription(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectSubscription(node)) + } + ast::CommentObject::ObjectTable(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTable(node)) + } + ast::CommentObject::ObjectTablespace(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTablespace(node)) + } + ast::CommentObject::ObjectTextSearchConfig(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTextSearchConfig(node)) + } + ast::CommentObject::ObjectTextSearchDict(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTextSearchDict(node)) + } + ast::CommentObject::ObjectTextSearchParser(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTextSearchParser(node)) + } + ast::CommentObject::ObjectTextSearchTemplate(node) => build_extension_member_object( + ast::ExtensionMemberObject::ObjectTextSearchTemplate(node), + ), + ast::CommentObject::ObjectTransform(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectTransform(node)) + } + ast::CommentObject::ObjectType(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectType(node)) } + ast::CommentObject::ObjectView(node) => { + build_extension_member_object(ast::ExtensionMemberObject::ObjectView(node)) + } + ast::CommentObject::ObjectColumn(node) => build_extension_member_value( + build_keyword_tokens([(node.column_token(), "column")]), + node.name().map(|name| build_extension_path_value(&name)), + ), + ast::CommentObject::ObjectConstraint(node) => { + let mut doc = build_extension_member_value( + build_keyword_tokens([(node.constraint_token(), "constraint")]), + node.constraint_name_ref() + .map(|name| build_extension_path_value(&name)), + ); + if let Some(on) = node.on_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&on)) + .append(Doc::text("on")); + } + if let Some(domain_token) = node.domain_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&domain_token)) + .append(Doc::text("domain")); + } + if let Some(domain) = node.domain_ref() { + doc = doc + .append(Doc::space()) + .append(build_extension_path_value(&domain)); + } else if let Some(table) = node.table_name_ref() { + doc = doc + .append(Doc::space()) + .append(build_extension_path_value(&table)); + } + doc.group() + } + ast::CommentObject::ObjectLargeObject(node) => build_extension_member_value( + build_keyword_tokens([ + (node.large_token(), "large"), + (node.object_token(), "object"), + ]), + node.literal() + .map(|literal| leading_comments(literal.syntax()).append(build_literal(literal))), + ), + ast::CommentObject::ObjectPolicy(node) => build_comment_object_on( + build_keyword_tokens([(node.policy_token(), "policy")]), + node.policy_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + node.on_token(), + node.table_name_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::CommentObject::ObjectPropertyGraph(node) => build_extension_member_value( + build_keyword_tokens([ + (node.property_token(), "property"), + (node.graph_token(), "graph"), + ]), + node.property_graph_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::CommentObject::ObjectRule(node) => build_comment_object_on( + build_keyword_tokens([(node.rule_token(), "rule")]), + node.rule_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + node.on_token(), + node.relation_name_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::CommentObject::ObjectTrigger(node) => build_comment_object_on( + build_keyword_tokens([(node.trigger_token(), "trigger")]), + node.trigger_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + node.on_token(), + node.relation_name_ref() + .map(|name| build_extension_path_value(&name)), + ), } } -fn build_grant_with_clause<'a>(with: ast::GrantWithClause) -> Doc<'a> { - let mut doc = Doc::text("with"); - if let Some(option) = with.grant_option() { +fn build_comment_object_on<'a>( + prefix: Doc<'a>, + value: Option>, + on: Option, + relation: Option>, +) -> Doc<'a> { + let mut doc = build_extension_member_value(prefix, value); + if let Some(on) = on { doc = doc - .append(Doc::space()) - .append(leading_comments(option.syntax())) - .append(build_keyword_node(option.syntax())); - } else if let Some(options) = with.grant_role_option_list() { - let options_doc = build_comma_separated_docs(options.grant_role_options().map(|option| { - let syntax = option.syntax().clone(); - let mut option_doc = option - .grant_role_option_name() - .map(|name| build_keyword_node(name.syntax())) - .unwrap_or_else(Doc::nil); - let value = option - .option_token() - .map(|token| (token, "option")) - .or_else(|| option.true_token().map(|token| (token, "true"))) - .or_else(|| option.false_token().map(|token| (token, "false"))); - if let Some((token, keyword)) = value { - option_doc = option_doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)); - } - (leading_comments(&syntax).append(option_doc), syntax) - })) - .unwrap_or_else(Doc::nil); - doc = doc.append(Doc::line_or_space().append(options_doc).nest(2)); + .append(Doc::line_or_space()) + .append(leading_comments_token(&on)) + .append(Doc::text("on")); + } + if let Some(relation) = relation { + doc = doc.append(Doc::space()).append(relation); } doc.group() } -fn build_granted_by_clause<'a>(granted: ast::GrantedByClause) -> Doc<'a> { - let mut doc = Doc::text("granted"); - if let Some(by) = granted.by_token() { +fn build_return_function_option<'a>(option: ast::ReturnFuncOption) -> Doc<'a> { + let mut doc = Doc::text("return"); + if let Some(expr) = option.expr() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&by)) - .append(Doc::text("by")); + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); } - if let Some(role) = granted.role_ref() { + doc.append(build_semicolon(option.semicolon_token())) +} + +fn build_literal_function_option<'a>( + keyword: &'static str, + literal: Option, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(literal) = literal { doc = doc .append(Doc::space()) - .append(leading_comments(role.syntax())) - .append(build_role_ref(&role)); + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); } - doc.group() + doc } -fn build_grant<'a>(grant: &ast::Grant) -> Doc<'a> { - let mut doc = Doc::text("grant"); - if let Some(privileges) = grant.privileges() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(privileges.syntax())) - .append(build_privileges(privileges)) - .nest(2), - ); - } - if let Some(on) = grant.on_privilege_objects_clause() { - let mut on_doc = Doc::text("on"); - if let Some(objects) = on.privilege_objects() { - on_doc = on_doc.append( - Doc::line_or_space() - .append(leading_comments(objects.syntax())) - .append(build_privilege_objects(objects)) - .nest(2), - ); +fn build_as_function_option<'a>(option: ast::AsFuncOption) -> Doc<'a> { + let mut doc = Doc::text("as"); + if let Some(target) = option.as_func_target() { + match target { + ast::AsFuncTarget::AsDefinition(definition) => { + if let Some(literal) = definition.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(definition.syntax())) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + } + ast::AsFuncTarget::AsObjFile(obj_file) => { + doc = doc + .append(Doc::space()) + .append(leading_comments(obj_file.syntax())); + if let Some(literal) = obj_file.obj_file() { + doc = doc + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + if let Some(comma) = obj_file.comma_token() { + doc = doc.append(comments_before(comma)).append(Doc::text(",")); + } + if let Some(literal) = obj_file.link_symbol() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + doc = doc.group(); + } } - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(on.syntax())) - .append(on_doc) - .nest(2), - ); } - if let Some(to) = grant.to_token() { + doc +} + +fn build_explain<'a>(explain: &ast::Explain) -> Doc<'a> { + let mut doc = Doc::text("explain"); + if let Some(mode) = explain.explain_mode() { + let mode_syntax = mode.syntax().clone(); + let parenthesized = matches!(&mode, ast::ExplainMode::ExplainOptionList(_)); + let mode_doc = match mode { + ast::ExplainMode::ExplainAnalyze(analyze) => { + let mut doc = if analyze.analyse_token().is_some() { + Doc::text("analyse") + } else { + Doc::text("analyze") + }; + if let Some(verbose) = analyze.explain_verbose() { + doc = doc + .append(Doc::space()) + .append(leading_comments(verbose.syntax())) + .append(build_keyword_node(verbose.syntax())); + } + doc + } + ast::ExplainMode::ExplainVerbose(verbose) => build_keyword_node(verbose.syntax()), + ast::ExplainMode::ExplainOptionList(options) => build_explain_option_list(options), + }; + let mode_doc = leading_comments(&mode_syntax).append(mode_doc); + doc = if parenthesized { + doc.append(Doc::space()).append(mode_doc) + } else { + doc.append(Doc::line_or_space().append(mode_doc).nest(2)) + }; + } + if let Some(stmt) = explain.explain_stmt() { doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&to)) - .append(Doc::text("to")) + Doc::hard_line() + .append(leading_comments(stmt.syntax())) + .append(build_explain_stmt(stmt)) .nest(2), ); } - if let Some(roles) = grant.role_ref_list() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(roles.syntax())) - .append(build_role_ref_list(roles)) - .nest(2), - ); - } - if let Some(with) = grant.grant_with_clause() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(with.syntax())) - .append(build_grant_with_clause(with)) - .nest(2), - ); - } - if let Some(granted) = grant.granted_by_clause() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(granted.syntax())) - .append(build_granted_by_clause(granted)) - .nest(2), - ); - } - doc.group().append(build_semicolon(grant.semicolon_token())) + doc.group() + .append(build_semicolon(explain.semicolon_token())) } -fn build_revoke_option_for<'a>(option: ast::RevokeOptionFor) -> Doc<'a> { - match option { - ast::RevokeOptionFor::AdminOptionFor(option) => build_keyword_node(option.syntax()), - ast::RevokeOptionFor::GrantOptionFor(option) => build_keyword_node(option.syntax()), - ast::RevokeOptionFor::InheritOptionFor(option) => build_keyword_node(option.syntax()), - ast::RevokeOptionFor::SetOptionFor(option) => build_keyword_node(option.syntax()), +fn build_explain_option_list<'a>(list: ast::ExplainOptionList) -> Doc<'a> { + let mut body = build_comma_separated_docs(list.explain_options().map(|option| { + let syntax = option.syntax().clone(); + let mut doc = option + .explain_option_name() + .map(|name| build_keyword_node(name.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(value) = option.explain_option_value() { + let value_doc = value + .expr() + .map(build_expr) + .unwrap_or_else(|| build_keyword_node(value.syntax())); + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(value_doc); + } + (leading_comments(&syntax).append(doc), syntax) + })) + .unwrap_or_else(Doc::nil); + if let Some(r_paren) = list.r_paren_token() { + body = body.append(comments_before(r_paren)); } + list.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(Doc::hard_line().append(body.group()).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) } -fn build_revoke<'a>(revoke: &ast::Revoke) -> Doc<'a> { - let mut doc = Doc::text("revoke"); - if let Some(option) = revoke.revoke_option_for() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(option.syntax())) - .append(build_revoke_option_for(option)) - .nest(2), - ); - } - if let Some(privileges) = revoke.privileges() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(privileges.syntax())) - .append(build_privileges(privileges)) - .nest(2), - ); +fn build_create_property_graph<'a>(stmt: &ast::CreatePropertyGraph) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.create_token(), "create")]); + if let Some(persistence) = stmt.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_persistence(persistence)); } - if let Some(on) = revoke.on_privilege_objects_clause() { - let mut on_doc = Doc::text("on"); - if let Some(objects) = on.privilege_objects() { - on_doc = on_doc.append( - Doc::line_or_space() - .append(leading_comments(objects.syntax())) - .append(build_privilege_objects(objects)) - .nest(2), - ); + for (token, keyword) in [ + (stmt.property_token(), "property"), + (stmt.graph_token(), "graph"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(on.syntax())) - .append(on_doc) - .nest(2), - ); - } - if let Some(from) = revoke.from_token() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&from)) - .append(Doc::text("from")) - .nest(2), - ); } - if let Some(roles) = revoke.role_ref_list() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(roles.syntax())) - .append(build_role_ref_list(roles)) - .nest(2), - ); + if let Some(graph) = stmt.property_graph() { + let graph_doc = graph + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(leading_comments(graph.syntax())) + .append(graph_doc); } - if let Some(granted) = revoke.granted_by_clause() { + if let Some(tables) = stmt.vertex_tables() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(granted.syntax())) - .append(build_granted_by_clause(granted)) + .append(leading_comments(tables.syntax())) + .append(build_vertex_tables(tables)) .nest(2), ); } - if let Some(behavior) = revoke.drop_behavior() { + if let Some(tables) = stmt.edge_tables() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(behavior.syntax())) - .append(build_drop_behavior(behavior)) + .append(leading_comments(tables.syntax())) + .append(build_edge_tables(tables)) .nest(2), ); } - doc.group() - .append(build_semicolon(revoke.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_import_table_filter<'a>(filter: ast::ImportTableFilter) -> Doc<'a> { - let (mut doc, names, l_paren, r_paren) = match filter { - ast::ImportTableFilter::ExceptTables(filter) => ( - Doc::text("except"), - filter - .remote_table_name_refs() - .map(|name| (name.syntax().clone(), build_name(name.syntax()))) - .collect::>(), - filter.l_paren_token(), - filter.r_paren_token(), - ), - ast::ImportTableFilter::LimitToTables(filter) => { - let mut doc = Doc::text("limit"); - if let Some(to) = filter.to_token() { +fn build_persistence<'a>(persistence: ast::Persistence) -> Doc<'a> { + match persistence { + ast::Persistence::Temp(temp) => { + let mut doc = Doc::nil(); + if let Some(token) = temp.local_token().or_else(|| temp.global_token()) { + let keyword = if token.kind() == SyntaxKind::LOCAL_KW { + "local" + } else { + "global" + }; doc = doc - .append(Doc::space()) - .append(leading_comments_token(&to)) - .append(Doc::text("to")); + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)) + .append(Doc::space()); } - ( - doc, - filter - .remote_table_name_refs() - .map(|name| (name.syntax().clone(), build_name(name.syntax()))) - .collect::>(), - filter.l_paren_token(), - filter.r_paren_token(), - ) + if let Some(token) = temp.temporary_token().or_else(|| temp.temp_token()) { + let keyword = if token.kind() == SyntaxKind::TEMPORARY_KW { + "temporary" + } else { + "temp" + }; + doc = doc + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + doc } - }; - let mut body = build_comma_separated_docs( - names - .into_iter() - .map(|(syntax, name)| (leading_comments(&syntax).append(name), syntax)), - ) - .unwrap_or_else(Doc::nil); - if let Some(r_paren) = r_paren { - body = body.append(comments_before(r_paren)); + ast::Persistence::Unlogged(unlogged) => unlogged + .unlogged_token() + .map(|token| leading_comments_token(&token).append(Doc::text("unlogged"))) + .unwrap_or_else(Doc::nil), } - doc = doc - .append(Doc::space()) - .append(l_paren.map(comments_before).unwrap_or_else(Doc::nil)) - .append(Doc::text("(")) - .append(wrap_body(body)) - .append(Doc::text(")")); - doc.group() } -fn build_import_foreign_schema<'a>(import: &ast::ImportForeignSchema) -> Doc<'a> { - let mut doc = Doc::text("import"); - for (token, keyword) in [ - (import.foreign_token(), "foreign"), - (import.schema_token(), "schema"), - ] { - if let Some(token) = token { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)) - .nest(2), - ); - } +fn build_create_role<'a>(stmt: &ast::CreateRole) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.create_token(), "create"), (stmt.role_token(), "role")]); + if let Some(role) = stmt.role() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_name(role.syntax())); } - if let Some(schema) = import.schema_ref() { + if let Some(options) = stmt.role_option_list() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(schema.syntax())) - .append(build_name(schema.syntax())) + .append(leading_comments(options.syntax())) + .append(build_role_option_list(&options)) .nest(2), ); } - if let Some(filter) = import.import_table_filter() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(filter.syntax())) - .append(build_import_table_filter(filter)) - .nest(2), - ); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_user<'a>(stmt: &ast::CreateUser) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.create_token(), "create"), (stmt.user_token(), "user")]); + if let Some(role) = stmt.role() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_name(role.syntax())); } - if let Some(from) = import.from_token() { + if let Some(options) = stmt.role_option_list() { doc = doc.append( Doc::line_or_space() - .append(leading_comments_token(&from)) - .append(Doc::text("from")) + .append(leading_comments(options.syntax())) + .append(build_role_option_list(&options)) .nest(2), ); } - if let Some(server) = import.server_clause() { + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_user_mapping<'a>(stmt: &ast::CreateUserMapping) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.user_token(), "user"), + (stmt.mapping_token(), "mapping"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + if let Some(token) = stmt.for_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("for")); + } + if let Some(role) = stmt.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + if let Some(server) = stmt.server_clause() { doc = doc.append( Doc::line_or_space() .append(leading_comments(server.syntax())) @@ -4460,253 +4761,830 @@ fn build_import_foreign_schema<'a>(import: &ast::ImportForeignSchema) -> Doc<'a> .nest(2), ); } - if let Some(into) = import.into_schema() { - let mut into_doc = Doc::text("into"); - if let Some(schema) = into.schema_ref() { - into_doc = into_doc - .append(Doc::space()) - .append(leading_comments(schema.syntax())) - .append(build_name(schema.syntax())); - } - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(into.syntax())) - .append(into_doc) - .nest(2), - ); - } - if let Some(options) = import.alter_option_list() { + if let Some(options) = stmt.alter_option_list() { doc = doc.append( Doc::line_or_space() .append(leading_comments(options.syntax())) - .append(build_alter_option_list(options)) + .append(build_alter_option_list(&options)) .nest(2), ); } - doc.group() - .append(build_semicolon(import.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_listen<'a>(listen: &ast::Listen) -> Doc<'a> { - let mut doc = Doc::text("listen"); - if let Some(channel) = listen.channel() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(channel.syntax())) - .append(build_name(channel.syntax())) - .nest(2), - ); +fn build_create_type<'a>(stmt: &ast::CreateType) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.create_token(), "create"), (stmt.type_token(), "type")]); + if let Some(name) = stmt.type_name() { + let name_doc = name + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(name_doc); } - doc.group() - .append(build_semicolon(listen.semicolon_token())) + if let Some(kind) = stmt.kind() { + let syntax = kind.syntax().clone(); + doc = doc + .append(Doc::space()) + .append(leading_comments(&syntax)) + .append(build_create_type_kind(kind)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_move<'a>(move_stmt: &ast::Move) -> Doc<'a> { - let mut doc = Doc::text("move"); - if let Some(action) = move_stmt.cursor_action() { +fn build_create_type_kind<'a>(kind: ast::CreateTypeKind) -> Doc<'a> { + match kind { + ast::CreateTypeKind::BaseType(base) => base + .attribute_list() + .map(|list| build_multiline_attribute_list(&list)) + .unwrap_or_else(Doc::nil), + ast::CreateTypeKind::CompositeType(composite) => { + let mut doc = Doc::text("as"); + if let Some(fields) = composite.composite_field_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(fields.syntax())) + .append(build_composite_field_list(fields)); + } + doc + } + ast::CreateTypeKind::EnumType(enum_type) => { + let mut doc = Doc::text("as"); + if let Some(token) = enum_type.enum_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("enum")); + } + if let Some(variants) = enum_type.variant_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(variants.syntax())) + .append(build_variant_list(variants)); + } + doc + } + ast::CreateTypeKind::RangeType(range) => { + let mut doc = Doc::text("as"); + if let Some(token) = range.range_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("range")); + } + if let Some(attributes) = range.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + doc + } + } + .group() +} + +fn build_composite_field_list<'a>(list: ast::CompositeFieldList) -> Doc<'a> { + let doc = list + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let fields = list.composite_field_defs().map(|field| { + let mut field_doc = field + .name() + .map(|name| build_name(name.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(ty) = field.ty() { + field_doc = field_doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + if let Some(collate) = field.collate() { + field_doc = field_doc + .append(Doc::line_or_space()) + .append(leading_comments(collate.syntax())) + .append(build_collate_expr(collate)); + } + ( + leading_comments(field.syntax()).append(field_doc.group()), + field.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(fields).unwrap_or_else(Doc::nil); + if let Some(token) = list.r_paren_token() { + body = body.append(comments_before(token)); + } + doc.append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) + .group() +} + +fn build_variant_list<'a>(list: ast::VariantList) -> Doc<'a> { + let doc = list + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let variants = list.variants().map(|variant| { + let variant_doc = variant + .literal() + .map(build_literal) + .unwrap_or_else(Doc::nil); + ( + leading_comments(variant.syntax()).append(variant_doc), + variant.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(variants).unwrap_or_else(Doc::nil); + if let Some(token) = list.r_paren_token() { + body = body.append(comments_before(token)); + } + doc.append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) + .group() +} + +fn build_create_rule<'a>(stmt: &ast::CreateRule) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.create_token(), "create")]); + if let Some(or_replace) = stmt.or_replace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(or_replace.syntax())) + .append(build_keyword_tokens([ + (or_replace.or_token(), "or"), + (or_replace.replace_token(), "replace"), + ])); + } + if let Some(token) = stmt.rule_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("rule")); + } + if let Some(rule) = stmt.rule() { + doc = doc + .append(Doc::space()) + .append(leading_comments(rule.syntax())) + .append(build_name(rule.syntax())); + } + let mut as_clause = stmt + .as_token() + .map(|token| leading_comments_token(&token).append(Doc::text("as"))) + .unwrap_or_else(Doc::nil); + let mut rule_on_tail = Doc::nil(); + if let Some(rule_on) = stmt.rule_on() { + let syntax = rule_on.syntax().clone(); + let (head, tail) = build_rule_on(rule_on); + as_clause = as_clause + .append(Doc::space()) + .append(leading_comments(&syntax)) + .append(head); + rule_on_tail = tail; + } + doc = doc.append( + Doc::line_or_space() + .append(as_clause.group()) + .nest(2) + .group(), + ); + doc = doc.append(rule_on_tail.nest(2)); + if let Some(rule_do) = stmt.rule_do() { doc = doc.append( - Doc::line_or_space() - .append(leading_comments(action.syntax())) - .append(build_cursor_action(action)) + Doc::hard_line() + .append(leading_comments(rule_do.syntax())) + .append(build_rule_do(rule_do)) .nest(2), ); } - if let Some(token) = move_stmt.from_token().or_else(|| move_stmt.in_token()) { - let keyword = if token.kind() == SyntaxKind::FROM_KW { - "from" + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_rule_on<'a>(rule_on: ast::RuleOn) -> (Doc<'a>, Doc<'a>) { + let mut head = Doc::text("on"); + if let Some(event) = rule_on.rule_event() { + let event_doc = if event.select_token().is_some() { + Doc::text("select") + } else if event.insert_token().is_some() { + Doc::text("insert") + } else if event.update_token().is_some() { + Doc::text("update") + } else if event.delete_token().is_some() { + Doc::text("delete") } else { - "in" + build_name(event.syntax()) }; - doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)) - .nest(2), - ); + head = head + .append(Doc::space()) + .append(leading_comments(event.syntax())) + .append(event_doc); } - if let Some(cursor) = move_stmt.cursor_ref() { - doc = doc.append( + + let mut tail = Doc::nil(); + if let Some(token) = rule_on.to_token() { + tail = tail + .append(Doc::hard_line()) + .append(leading_comments_token(&token)) + .append(Doc::text("to")); + } + if let Some(relation) = rule_on.relation_name_ref() { + tail = tail + .append(Doc::space()) + .append(build_extension_path_value(&relation)); + } + if let Some(where_clause) = rule_on.where_clause() { + tail = tail.append( Doc::line_or_space() - .append(leading_comments(cursor.syntax())) - .append(build_name(cursor.syntax())) + .append(leading_comments(where_clause.syntax())) + .append(build_rule_where_clause(where_clause)) .nest(2), ); } + (head, tail.group()) +} + +fn build_rule_where_clause<'a>(where_clause: ast::WhereClause) -> Doc<'a> { + let mut doc = where_clause + .where_token() + .map(|token| leading_comments_token(&token).append(Doc::text("where"))) + .unwrap_or_else(Doc::nil); + if let Some(expr) = where_clause.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + doc +} + +fn build_rule_do<'a>(rule_do: ast::RuleDo) -> Doc<'a> { + let mut doc = Doc::text("do"); + if let Some(token) = rule_do.also_token().or_else(|| rule_do.instead_token()) { + let keyword = if token.kind() == SyntaxKind::ALSO_KW { + "also" + } else { + "instead" + }; + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + if let Some(action) = rule_do.rule_action() { + let syntax = action.syntax().clone(); + doc = doc + .append(Doc::space()) + .append(leading_comments(&syntax)) + .append(build_rule_action(action)); + } doc.group() - .append(build_semicolon(move_stmt.semicolon_token())) } -fn build_notify<'a>(notify: &ast::Notify) -> Doc<'a> { - let mut doc = Doc::text("notify"); - if let Some(channel) = notify.channel_ref() { +fn build_rule_action<'a>(action: ast::RuleAction) -> Doc<'a> { + match action { + ast::RuleAction::Nothing(_) => Doc::text("nothing"), + ast::RuleAction::RuleStmt(stmt) => build_rule_stmt(stmt), + ast::RuleAction::RuleStmtList(list) => { + let items = list + .rule_stmts() + .map(|stmt| leading_comments(stmt.syntax()).append(build_rule_stmt(stmt))); + let mut body = Doc::list(Itertools::intersperse(items, Doc::hard_line()).collect()); + if let Some(token) = list.r_paren_token() { + body = body.append(comments_before(token)); + } + list.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) + } + } +} + +fn build_rule_stmt<'a>(stmt: ast::RuleStmt) -> Doc<'a> { + match stmt { + ast::RuleStmt::Delete(stmt) => build_delete(&stmt), + ast::RuleStmt::Insert(stmt) => build_insert(&stmt), + ast::RuleStmt::Notify(stmt) => build_notify(&stmt), + ast::RuleStmt::Update(stmt) => build_update(&stmt), + ast::RuleStmt::SelectVariant(stmt) => build_select_variant(stmt), + } +} + +fn build_create_schema<'a>(stmt: &ast::CreateSchema) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.schema_token(), "schema"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + if let Some(target) = stmt.create_schema_target() { + let syntax = target.syntax().clone(); doc = doc.append( Doc::line_or_space() - .append(leading_comments(channel.syntax())) - .append(build_name(channel.syntax())) + .append(leading_comments(&syntax)) + .append(build_create_schema_target(target)) .nest(2), ); } - if let Some(comma) = notify.comma_token() { - doc = doc.append(comments_before(comma)).append(Doc::text(",")); - } - if let Some(payload) = notify.literal() { + for element in stmt.schema_elements() { + let syntax = element.syntax().clone(); doc = doc.append( - Doc::line_or_space() - .append(leading_comments(payload.syntax())) - .append(build_literal(payload)) + Doc::hard_line() + .append(leading_comments(&syntax)) + .append(build_schema_element(element)) .nest(2), ); } - doc.group() - .append(build_semicolon(notify.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_reassign<'a>(reassign: &ast::Reassign) -> Doc<'a> { - let mut doc = Doc::text("reassign"); - for (token, keyword) in [ - (reassign.owned_token(), "owned"), - (reassign.by_token(), "by"), - ] { - if let Some(token) = token { - doc = doc.append( - Doc::line_or_space() +fn build_create_schema_target<'a>(target: ast::CreateSchemaTarget) -> Doc<'a> { + match target { + ast::CreateSchemaTarget::AuthorizationSchema(target) => { + let mut doc = target + .authorization_token() + .map(|token| leading_comments_token(&token).append(Doc::text("authorization"))) + .unwrap_or_else(Doc::nil); + if let Some(role) = target.role() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_name(role.syntax())); + } + doc + } + ast::CreateSchemaTarget::NamedSchema(target) => { + let mut doc = target + .schema() + .map(|schema| leading_comments(schema.syntax()).append(build_name(schema.syntax()))) + .unwrap_or_else(Doc::nil); + if let Some(token) = target.authorization_token() { + doc = doc + .append(Doc::line_or_space()) .append(leading_comments_token(&token)) - .append(Doc::text(keyword)) - .nest(2), - ); + .append(Doc::text("authorization")); + } + if let Some(role) = target.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + doc.group() } } - if let Some(roles) = reassign.before() { +} + +fn build_schema_element<'a>(element: ast::SchemaElement) -> Doc<'a> { + match element { + ast::SchemaElement::CreateIndex(stmt) => build_create_index(&stmt), + ast::SchemaElement::CreateSequence(stmt) => build_create_sequence(&stmt), + ast::SchemaElement::CreateTable(stmt) => build_create_table(&stmt), + ast::SchemaElement::CreateTrigger(stmt) => build_create_trigger(&stmt), + ast::SchemaElement::CreateView(stmt) => build_create_view(&stmt), + ast::SchemaElement::Grant(stmt) => build_grant(&stmt), + } +} + +fn build_create_sequence<'a>(stmt: &ast::CreateSequence) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.create_token(), "create")]); + if let Some(persistence) = stmt.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_persistence(persistence)); + } + if let Some(token) = stmt.sequence_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("sequence")); + } + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + if let Some(sequence) = stmt.sequence() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(roles.syntax())) - .append(build_role_ref_list(roles)) + .append(leading_comments(sequence.syntax())) + .append( + sequence + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) .nest(2), ); } - if let Some(to) = reassign.to_token() { + for option in stmt.sequence_options() { + let syntax = option.syntax().clone(); doc = doc.append( Doc::line_or_space() - .append(leading_comments_token(&to)) - .append(Doc::text("to")) + .append(leading_comments(&syntax)) + .append(build_sequence_option(option)) .nest(2), ); } - if let Some(roles) = reassign.after() { + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_server<'a>(stmt: &ast::CreateServer) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.server_token(), "server"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + if let Some(server) = stmt.server() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(roles.syntax())) - .append(build_role_ref_list(roles)) + .append(leading_comments(server.syntax())) + .append(build_name(server.syntax())) .nest(2), ); } - doc.group() - .append(build_semicolon(reassign.semicolon_token())) -} - -fn build_refresh<'a>(refresh: &ast::Refresh) -> Doc<'a> { - let mut doc = Doc::text("refresh"); - for (token, keyword) in [ - (refresh.materialized_token(), "materialized"), - (refresh.view_token(), "view"), - (refresh.concurrently_token(), "concurrently"), - ] { - if let Some(token) = token { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)) - .nest(2), - ); + if let Some(server_type) = stmt.server_type() { + let mut clause = build_keyword_tokens([(server_type.type_token(), "type")]); + if let Some(literal) = server_type.literal() { + clause = clause + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(server_type.syntax())) + .append(clause) + .nest(2), + ); } - if let Some(view) = refresh.view_ref() { - let view_doc = view - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); + if let Some(version) = stmt.server_version() { + let clause = append_optional_literal( + build_keyword_tokens([(version.version_token(), "version")]), + version.literal(), + ); doc = doc.append( Doc::line_or_space() - .append(leading_comments(view.syntax())) - .append(view_doc) + .append(leading_comments(version.syntax())) + .append(clause) .nest(2), ); } - if let Some(data) = refresh.data_option() { + let mut wrapper: Option> = None; + for (token, keyword) in [ + (stmt.foreign_token(), "foreign"), + (stmt.data_token(), "data"), + (stmt.wrapper_token(), "wrapper"), + ] { + if let Some(token) = token { + let keyword = leading_comments_token(&token).append(Doc::text(keyword)); + wrapper = Some(match wrapper { + Some(wrapper) => wrapper.append(Doc::space()).append(keyword), + None => keyword, + }); + } + } + if let Some(name) = stmt.foreign_data_wrapper_ref() { + let name = leading_comments(name.syntax()).append(build_name(name.syntax())); + wrapper = Some(match wrapper { + Some(wrapper) => wrapper.append(Doc::space()).append(name), + None => name, + }); + } + if let Some(wrapper) = wrapper { + doc = doc.append(Doc::line_or_space().append(wrapper).nest(2)); + } + if let Some(options) = stmt.alter_option_list() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(data.syntax())) - .append(build_keyword_node(data.syntax())) + .append(leading_comments(options.syntax())) + .append(build_alter_option_list(&options)) .nest(2), ); } - doc.group() - .append(build_semicolon(refresh.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_repack<'a>(repack: &ast::Repack) -> Doc<'a> { - let mut doc = Doc::text("repack"); - if let Some(options) = repack.option_item_list() { +fn build_create_statistics<'a>(stmt: &ast::CreateStatistics) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.statistics_token(), "statistics"), + ]); + if let Some(if_not_exists) = stmt.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + let mut statistics_doc = stmt.statistics().map(|statistics| { + let name = statistics + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + leading_comments(statistics.syntax()).append(name) + }); + if let Some(types) = stmt.stat_types() { + let types = leading_comments(types.syntax()).append(build_stat_types(types)); + statistics_doc = Some(match statistics_doc { + Some(statistics_doc) => statistics_doc.append(Doc::space()).append(types), + None => types, + }); + } + if let Some(statistics_doc) = statistics_doc { + doc = doc + .append(Doc::line_or_space().append(statistics_doc).nest(2)) + .group(); + } + if let Some(on) = stmt.statistics_on_clause() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(options.syntax())) - .append(build_option_item_list(options)) + .append(leading_comments(on.syntax())) + .append(build_statistics_on_clause(on)) .nest(2), ); } - if let Some(tables) = repack.table_and_columns_list() { + if let Some(from) = stmt.from_table() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(tables.syntax())) - .append(build_table_and_columns_list(tables)) + .append(leading_comments(from.syntax())) + .append(build_statistics_from_table(from)) .nest(2), ); } - if let Some(using_index) = repack.using_index() { - let mut using_doc = Doc::text("using"); - if let Some(index) = using_index.index_token() { - using_doc = using_doc + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_stat_types<'a>(types: ast::StatTypes) -> Doc<'a> { + let mut doc = types + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let kinds = types.statistics_kinds().map(|kind| { + ( + leading_comments(kind.syntax()).append(build_name(kind.syntax())), + kind.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(kinds).unwrap_or_else(Doc::nil); + if let Some(r_paren) = types.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc = doc.append(wrap_body(body)).append(Doc::text(")")); + doc.group() +} + +fn build_statistics_on_clause<'a>(on: ast::StatisticsOnClause) -> Doc<'a> { + let mut doc = build_keyword_tokens([(on.on_token(), "on")]); + let exprs = on.exprs().map(|expr| { + let syntax = expr.syntax().clone(); + (leading_comments(&syntax).append(build_expr(expr)), syntax) + }); + if let Some(exprs) = build_comma_separated_docs(exprs) { + doc = doc.append(Doc::line_or_space().append(exprs).nest(2)); + } + doc.group() +} + +fn build_statistics_from_table<'a>(from: ast::FromTable) -> Doc<'a> { + let mut doc = build_keyword_tokens([(from.from_token(), "from")]); + if let Some(table) = from.table_name_ref() { + doc = doc + .append(Doc::space()) + .append(build_extension_path_value(&table)); + } + doc +} + +fn build_create_tablespace<'a>(stmt: &ast::CreateTablespace) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.tablespace_token(), "tablespace"), + ]); + if let Some(tablespace) = stmt.tablespace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(tablespace.syntax())) + .append(build_name(tablespace.syntax())); + } + if let Some(owner) = stmt.tablespace_owner() { + let mut clause = build_keyword_tokens([(owner.owner_token(), "owner")]); + if let Some(role) = owner.role_ref() { + clause = clause .append(Doc::space()) - .append(leading_comments_token(&index)) - .append(Doc::text("index")); + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); } - if let Some(index) = using_index.index_ref() { - let name = index - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - using_doc = using_doc + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(owner.syntax())) + .append(clause) + .nest(2), + ); + } + if let Some(location) = stmt.location_token() { + let mut clause = leading_comments_token(&location).append(Doc::text("location")); + if let Some(literal) = stmt.literal() { + clause = clause .append(Doc::space()) - .append(leading_comments(index.syntax())) - .append(name); + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); } + doc = doc.append(Doc::hard_line().append(clause).nest(2)); + } + if let Some(params) = stmt.with_params() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(using_index.syntax())) - .append(using_doc) + .append(leading_comments(params.syntax())) + .append(build_with_params(params)) .nest(2), ); } - doc.group() - .append(build_semicolon(repack.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_reset_role<'a>(reset: &ast::ResetRole) -> Doc<'a> { - let mut doc = Doc::text("reset"); - if let Some(role) = reset.role_token() { +fn build_create_text_search_configuration<'a>( + stmt: &ast::CreateTextSearchConfiguration, +) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.configuration_token(), "configuration"), + ]); + if let Some(name) = stmt.text_search_configuration() { + let name_doc = name + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append( + Doc::line_or_space() + .append(leading_comments(name.syntax())) + .append(name_doc) + .nest(2), + ) + .group(); + } + if let Some(attributes) = stmt.attribute_list() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&role)) - .append(Doc::text("role")); + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); } - doc.append(build_semicolon(reset.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_reset_session_auth<'a>(reset: &ast::ResetSessionAuth) -> Doc<'a> { - let mut doc = Doc::text("reset"); +fn build_create_text_search_dictionary<'a>(stmt: &ast::CreateTextSearchDictionary) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.dictionary_token(), "dictionary"), + ]); + if let Some(name) = stmt.text_search_dictionary() { + let name_doc = name + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(name_doc); + } + if let Some(attributes) = stmt.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_text_search_parser<'a>(stmt: &ast::CreateTextSearchParser) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.parser_token(), "parser"), + ]); + if let Some(name) = stmt.text_search_parser() { + let name_doc = name + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(name_doc); + } + if let Some(attributes) = stmt.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_text_search_template<'a>(stmt: &ast::CreateTextSearchTemplate) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.template_token(), "template"), + ]); + if let Some(name) = stmt.text_search_template() { + let name_doc = name + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(name_doc); + } + if let Some(attributes) = stmt.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_group<'a>(stmt: &ast::CreateGroup) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.group_token(), "group"), + ]); + if let Some(role) = stmt.role() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(role.syntax())) + .append(build_name(role.syntax())) + .nest(2), + ); + } + if let Some(options) = stmt.role_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_role_option_list(&options)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_create_language<'a>(stmt: &ast::CreateLanguage) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.create_token(), "create")]); + if let Some(or_replace) = stmt.or_replace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(or_replace.syntax())) + .append(build_keyword_node(or_replace.syntax())); + } for (token, keyword) in [ - (reset.session_token(), "session"), - (reset.authorization_token(), "authorization"), + (stmt.trusted_token(), "trusted"), + (stmt.procedural_token(), "procedural"), + (stmt.language_token(), "language"), ] { if let Some(token) = token { doc = doc @@ -4715,4118 +5593,12028 @@ fn build_reset_session_auth<'a>(reset: &ast::ResetSessionAuth) -> Doc<'a> { .append(Doc::text(keyword)); } } - doc.append(build_semicolon(reset.semicolon_token())) -} - -fn append_security_object_value<'a>(prefix: Doc<'a>, value: Doc<'a>) -> Doc<'a> { - prefix - .append(Doc::line_or_space().append(value).nest(2)) - .group() -} - -fn build_security_object_value<'a>(node: &impl AstNode, value: Doc<'a>) -> Doc<'a> { - leading_comments(node.syntax()).append(value) + if let Some(language) = stmt.language() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(language.syntax())) + .append(build_name(language.syntax())) + .nest(2), + ); + } + if let Some(handler) = stmt.handler_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(handler.syntax())) + .append(build_language_function_clause( + handler.handler_token(), + "handler", + handler.function_name_ref(), + )) + .nest(2), + ); + } + if let Some(inline) = stmt.inline_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(inline.syntax())) + .append(build_language_function_clause( + inline.inline_token(), + "inline", + inline.function_name_ref(), + )) + .nest(2), + ); + } + if let Some(validator) = stmt.validator_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(validator.syntax())) + .append(build_language_function_clause( + validator.validator_token(), + "validator", + validator.function_name_ref(), + )) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_aggregate_sig<'a>(aggregate: ast::Aggregate) -> Doc<'a> { - let mut doc = aggregate - .path_ref() - .map(|path| build_path_ref(&path)) +fn build_language_function_clause<'a>( + token: Option, + keyword: &'static str, + function: Option, +) -> Doc<'a> { + let mut doc = token + .map(|token| leading_comments_token(&token).append(Doc::text(keyword))) .unwrap_or_else(Doc::nil); - if let Some(params) = aggregate.param_list() { + if let Some(function) = function { doc = doc - .append(leading_comments(params.syntax())) - .append(build_function_param_list(params)); + .append(Doc::space()) + .append(build_extension_path_value(&function)); } doc } -fn build_security_label_object<'a>(object: ast::SecurityLabelObject) -> Doc<'a> { - match object { - ast::SecurityLabelObject::ObjectAggregate(node) => { - let value = node - .aggregate() - .map(|value| { - let doc = build_aggregate_sig(value.clone()); - build_security_object_value(&value, doc) - }) - .unwrap_or_else(Doc::nil); - append_security_object_value( - build_keyword_tokens([(node.aggregate_token(), "aggregate")]), - value, - ) - } - ast::SecurityLabelObject::ObjectColumn(node) => { - let value = node - .name() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil); - append_security_object_value( - build_keyword_tokens([(node.column_token(), "column")]), - value, - ) - } - ast::SecurityLabelObject::ObjectDatabase(node) => append_security_object_value( - build_keyword_tokens([(node.database_token(), "database")]), - node.database_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectDomain(node) => append_security_object_value( - build_keyword_tokens([(node.domain_token(), "domain")]), - node.domain_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectEventTrigger(node) => append_security_object_value( - build_keyword_tokens([ - (node.event_token(), "event"), - (node.trigger_token(), "trigger"), - ]), - node.event_trigger_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectForeignTable(node) => append_security_object_value( - build_keyword_tokens([ - (node.foreign_token(), "foreign"), - (node.table_token(), "table"), - ]), - node.table_name_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectFunction(node) => append_security_object_value( - build_keyword_tokens([(node.function_token(), "function")]), - node.function_sig() - .map(|value| { - let doc = build_function_sig(value.clone()); - build_security_object_value(&value, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectLanguage(node) => append_security_object_value( - build_keyword_tokens([ - (node.procedural_token(), "procedural"), - (node.language_token(), "language"), - ]), - node.language_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectLargeObject(node) => append_security_object_value( - build_keyword_tokens([ - (node.large_token(), "large"), - (node.object_token(), "object"), - ]), - node.literal() - .map(|value| { - let doc = build_literal(value.clone()); - build_security_object_value(&value, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectMaterializedView(node) => append_security_object_value( - build_keyword_tokens([ - (node.materialized_token(), "materialized"), - (node.view_token(), "view"), - ]), - node.view_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectProcedure(node) => append_security_object_value( - build_keyword_tokens([(node.procedure_token(), "procedure")]), - node.procedure_sig() - .map(|value| { - let doc = build_procedure_sig(value.clone()); - build_security_object_value(&value, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectPublication(node) => append_security_object_value( - build_keyword_tokens([(node.publication_token(), "publication")]), - node.publication_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectRole(node) => append_security_object_value( - build_keyword_tokens([(node.role_token(), "role")]), - node.role_ref() - .map(|role| { - let doc = build_role_ref(&role); - build_security_object_value(&role, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectRoutine(node) => append_security_object_value( - build_keyword_tokens([(node.routine_token(), "routine")]), - node.routine_sig() - .map(|value| { - let doc = build_routine_sig(value.clone()); - build_security_object_value(&value, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectSchema(node) => append_security_object_value( - build_keyword_tokens([(node.schema_token(), "schema")]), - node.schema_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectSequence(node) => append_security_object_value( - build_keyword_tokens([(node.sequence_token(), "sequence")]), - node.sequence_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectSubscription(node) => append_security_object_value( - build_keyword_tokens([(node.subscription_token(), "subscription")]), - node.subscription_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectTable(node) => append_security_object_value( - build_keyword_tokens([(node.table_token(), "table")]), - node.table_name_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectTablespace(node) => append_security_object_value( - build_keyword_tokens([(node.tablespace_token(), "tablespace")]), - node.tablespace_ref() - .map(|name| build_security_object_value(&name, build_name(name.syntax()))) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectType(node) => append_security_object_value( - build_keyword_tokens([(node.type_token(), "type")]), - node.type_name_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), - ast::SecurityLabelObject::ObjectView(node) => append_security_object_value( - build_keyword_tokens([(node.view_token(), "view")]), - node.view_ref() - .map(|name| { - let doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - build_security_object_value(&name, doc) - }) - .unwrap_or_else(Doc::nil), - ), +fn build_create_operator<'a>(stmt: &ast::CreateOperator) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.operator_token(), "operator"), + ]); + if let Some(op) = stmt.op() { + doc = doc + .append(Doc::space()) + .append(leading_comments(op.syntax())) + .append(build_operator(&op)); + } + if let Some(attributes) = stmt.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_multiline_attribute_list(&attributes)); } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_security_label<'a>(label: &ast::SecurityLabel) -> Doc<'a> { - let mut doc = Doc::text("security"); - if let Some(token) = label.label_token() { +fn build_create_operator_class<'a>(stmt: &ast::CreateOperatorClass) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.operator_token(), "operator"), + (stmt.class_token(), "class"), + ]); + if let Some(class) = stmt.op_class_name() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("label")); + .append(leading_comments(class.syntax())) + .append( + class + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ); } - if let Some(provider) = label.for_provider() { - let mut provider_doc = Doc::text("for"); - if let Some(name) = provider.security_label_provider() { - provider_doc = provider_doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } else if let Some(literal) = provider.literal() { - provider_doc = provider_doc + + let has_type_clause = stmt.default_token().is_some() + || stmt.for_token().is_some() + || stmt.type_token().is_some() + || stmt.ty().is_some(); + if has_type_clause { + let mut clause = build_keyword_tokens([ + (stmt.default_token(), "default"), + (stmt.for_token(), "for"), + (stmt.type_token(), "type"), + ]); + if let Some(ty) = stmt.ty() { + clause = clause .append(Doc::space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); } + doc = doc.append(Doc::hard_line().append(clause).nest(2)); + } + if let Some(using) = stmt.using_method() { doc = doc.append( - Doc::line_or_space() - .append(leading_comments(provider.syntax())) - .append(provider_doc) + Doc::hard_line() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) .nest(2), ); } - let object = label.security_label_object(); - if let Some(on) = label.on_token() { - let mut on_doc = leading_comments_token(&on).append(Doc::text("on")); - if let Some(object) = object { - on_doc = on_doc + if let Some(family) = stmt.operator_family_clause() { + let mut family_doc = build_keyword_tokens([(family.family_token(), "family")]); + if let Some(name) = family.op_family_ref() { + family_doc = family_doc .append(Doc::space()) - .append(leading_comments(object.syntax())) - .append(build_security_label_object(object)); + .append(build_extension_path_value(&name)); } - doc = doc.append(Doc::line_or_space().append(on_doc).nest(2)); - } else if let Some(object) = object { doc = doc.append( - Doc::line_or_space() - .append(leading_comments(object.syntax())) - .append(build_security_label_object(object)) + Doc::hard_line() + .append(leading_comments(family.syntax())) + .append(family_doc) .nest(2), ); } + if let Some(as_token) = stmt.as_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")); + } + if let Some(options) = stmt.operator_class_option_list() { + let option_docs = options.op_class_options().map(|option| { + let syntax = option.syntax().clone(); + ( + leading_comments(&syntax).append(build_op_class_option(option)), + syntax, + ) + }); + if let Some(options_doc) = build_comma_separated_docs(option_docs) { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(options.syntax())) + .append(options_doc) + .nest(2), + ); + } + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} - let value = if let Some(literal) = label.literal() { - Some(leading_comments(literal.syntax()).append(build_literal(literal))) - } else { - label - .null_token() - .map(|null| leading_comments_token(&null).append(Doc::text("null"))) - }; - if let Some(is) = label.is_token() { - let mut is_doc = leading_comments_token(&is).append(Doc::text("is")); - if let Some(value) = value { - is_doc = is_doc.append(Doc::space()).append(value); - } - doc = doc.append(Doc::line_or_space().append(is_doc).nest(2)); - } else if let Some(value) = value { - doc = doc.append(Doc::line_or_space().append(value).nest(2)); +fn build_create_operator_family<'a>(stmt: &ast::CreateOperatorFamily) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.operator_token(), "operator"), + (stmt.family_token(), "family"), + ]); + if let Some(family) = stmt.op_family_name() { + doc = doc + .append( + Doc::line_or_space() + .append(leading_comments(family.syntax())) + .append( + family + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + .nest(2), + ) + .group(); } - doc.group().append(build_semicolon(label.semicolon_token())) -} - -fn build_set_constraints<'a>(set: &ast::SetConstraints) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(constraints) = set.constraints_token() { + if let Some(using) = stmt.using_method() { doc = doc.append( - Doc::line_or_space() - .append(leading_comments_token(&constraints)) - .append(Doc::text("constraints")) + Doc::hard_line() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) .nest(2), ); } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} - let names = set.constraint_name_refs().map(|name| { - let syntax = name.syntax().clone(); - let name_doc = name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil); - (leading_comments(&syntax).append(name_doc), syntax) - }); - let target = if let Some(names) = build_comma_separated_docs(names) { - Some(names) - } else { - set.all_token() - .map(|all| leading_comments_token(&all).append(Doc::text("all"))) - }; - if let Some(target) = target { - doc = doc.append(Doc::line_or_space().append(target).nest(2)); +fn build_create_policy<'a>(stmt: &ast::CreatePolicy) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.create_token(), "create"), + (stmt.policy_token(), "policy"), + ]); + if let Some(policy) = stmt.policy() { + doc = doc + .append(Doc::space()) + .append(leading_comments(policy.syntax())) + .append(build_name(policy.syntax())); } - if let Some(timing) = set.constraint_timing() { + if let Some(on) = stmt.on_table() { + let mut on_doc = build_keyword_tokens([(on.on_token(), "on")]); + if let Some(table) = on.table_name_ref() { + on_doc = on_doc + .append(Doc::space()) + .append(build_extension_path_value(&table)); + } doc = doc.append( Doc::line_or_space() - .append(leading_comments(timing.syntax())) - .append(build_keyword_node(timing.syntax())) + .append(leading_comments(on.syntax())) + .append(on_doc) .nest(2), ); } - doc.group().append(build_semicolon(set.semicolon_token())) -} - -fn build_role_ref<'a>(role: &ast::RoleRef) -> Doc<'a> { - if let Some(group) = role.group_token() { - let mut doc = leading_comments_token(&group).append(Doc::text("group")); - if let Some(ident) = role.ident_token() { - doc = doc + if let Some(policy_type) = stmt.as_policy_type() { + let mut type_doc = build_keyword_tokens([(policy_type.as_token(), "as")]); + if let Some(ident) = policy_type.ident_token() { + type_doc = type_doc .append(Doc::space()) .append(leading_comments_token(&ident)) - .append(build_name(role.syntax())); + .append(Doc::text(ident.text().to_ascii_lowercase())); } - doc - } else if role.ident_token().is_some() { - build_name(role.syntax()) - } else { - build_keyword_node(role.syntax()) + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(policy_type.syntax())) + .append(type_doc) + .nest(2), + ); } -} - -fn build_set_role_target<'a>(target: ast::SetRoleTarget) -> Doc<'a> { - match target { - ast::SetRoleTarget::Literal(literal) => build_literal(literal), - ast::SetRoleTarget::RoleRef(role) => build_role_ref(&role), - ast::SetRoleTarget::SetRoleNone(none) => build_keyword_node(none.syntax()), + if let Some(command) = stmt.policy_command() { + let mut command_doc = build_keyword_tokens([(command.for_token(), "for")]); + if let Some(kind) = command.command() { + let syntax = kind.syntax().clone(); + command_doc = command_doc + .append(Doc::space()) + .append(leading_comments(&syntax)) + .append(build_keyword_node(&syntax)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(command.syntax())) + .append(command_doc) + .nest(2), + ); } -} - -fn build_set_role<'a>(set: &ast::SetRole) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(scope) = set.set_scope() { + if let Some(roles) = stmt.policy_roles() { + let mut roles_doc = build_keyword_tokens([(roles.to_token(), "to")]); + if let Some(list) = roles.role_ref_list() { + roles_doc = roles_doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_role_ref_list(list)); + } doc = doc.append( Doc::line_or_space() - .append(leading_comments(scope.syntax())) - .append(build_keyword_node(scope.syntax())) + .append(leading_comments(roles.syntax())) + .append(roles_doc) .nest(2), ); } - if let Some(role) = set.role_token() { + if let Some(using) = stmt.using_expr_clause() { doc = doc.append( Doc::line_or_space() - .append(leading_comments_token(&role)) - .append(Doc::text("role")) + .append(leading_comments(using.syntax())) + .append(build_policy_expr_clause( + build_keyword_tokens([(using.using_token(), "using")]), + using.l_paren_token(), + using.expr(), + using.r_paren_token(), + )) .nest(2), ); } - if let Some(target) = set.set_role_target() { - let comments = leading_comments(target.syntax()); + if let Some(check) = stmt.with_check_expr_clause() { doc = doc.append( Doc::line_or_space() - .append(comments) - .append(build_set_role_target(target)) + .append(leading_comments(check.syntax())) + .append(build_policy_expr_clause( + build_keyword_tokens([ + (check.with_token(), "with"), + (check.check_token(), "check"), + ]), + check.l_paren_token(), + check.expr(), + check.r_paren_token(), + )) .nest(2), ); } - doc.group().append(build_semicolon(set.semicolon_token())) + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_set_session_auth_target<'a>(target: ast::SetSessionAuthTarget) -> Doc<'a> { - match target { - ast::SetSessionAuthTarget::Literal(literal) => build_literal(literal), - ast::SetSessionAuthTarget::RoleRef(role) => build_role_ref(&role), - ast::SetSessionAuthTarget::SetSessionAuthDefault(default) => { - build_keyword_node(default.syntax()) +fn build_create_procedure<'a>(stmt: &ast::CreateProcedure) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.create_token(), "create")]); + if let Some(or_replace) = stmt.or_replace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(or_replace.syntax())) + .append(build_keyword_node(or_replace.syntax())); + } + if let Some(procedure) = stmt.procedure_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&procedure)) + .append(Doc::text("procedure")); + } + if let Some(name) = stmt.name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ); + } + if let Some(params) = stmt.param_list() { + doc = doc + .append(leading_comments(params.syntax())) + .append(build_function_param_list(params)); + } + doc = doc.group(); + if let Some(options) = stmt.option_list() { + let mut option_leading = leading_comments(options.syntax()); + for option in options.options() { + doc = doc.append( + Doc::hard_line() + .append(option_leading) + .append(leading_comments(option.syntax())) + .append(build_function_option(option)) + .nest(2), + ); + option_leading = Doc::nil(); } } + doc.append(build_semicolon(stmt.semicolon_token())) } -fn build_set_session_auth<'a>(set: &ast::SetSessionAuth) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(scope) = set.set_scope() { +fn build_create_materialized_view<'a>(view: &ast::CreateMaterializedView) -> Doc<'a> { + let mut doc = Doc::text("create"); + for (token, keyword) in [ + (view.materialized_token(), "materialized"), + (view.view_token(), "view"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(if_not_exists) = view.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + let mut view_doc = view.view().and_then(|name| { + name.path() + .map(|path| leading_comments(name.syntax()).append(build_path(&path))) + }); + if let Some(columns) = view.column_list() { + let columns = leading_comments(columns.syntax()).append(build_cte_column_list(columns)); + view_doc = Some(match view_doc { + Some(view_doc) => view_doc.append(Doc::space()).append(columns), + None => columns, + }); + } + if let Some(view_doc) = view_doc { + doc = doc + .append(Doc::line_or_space().append(view_doc).nest(2)) + .group(); + } + let has_options = view.using_method().is_some() + || view.with_params().is_some() + || view.tablespace_clause().is_some(); + if let Some(using) = view.using_method() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(scope.syntax())) - .append(build_keyword_node(scope.syntax())) + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) .nest(2), ); } - if let Some(session) = set.session_token() { + if let Some(params) = view.with_params() { doc = doc.append( Doc::line_or_space() - .append(leading_comments_token(&session)) - .append(Doc::text("session")) + .append(leading_comments(params.syntax())) + .append(build_with_params(params)) .nest(2), ); } - if let Some(authorization) = set.authorization_token() { + if let Some(tablespace) = view.tablespace_clause() { + let mut option = Doc::text("tablespace"); + if let Some(name) = tablespace.tablespace_ref() { + option = option + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } doc = doc.append( Doc::line_or_space() - .append(leading_comments_token(&authorization)) - .append(Doc::text("authorization")) + .append(leading_comments(tablespace.syntax())) + .append(option) .nest(2), ); } - if let Some(target) = set.set_session_auth_target() { - let comments = leading_comments(target.syntax()); + if let Some(as_token) = view.as_token() { + let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); + doc = if has_options { + doc.append(Doc::hard_line().append(as_doc).nest(2)) + } else { + doc.append(Doc::space()).append(as_doc) + }; + } + if let Some(query) = view.query() { doc = doc.append( - Doc::line_or_space() - .append(comments) - .append(build_set_session_auth_target(target)) + Doc::hard_line() + .append(leading_comments(query.syntax())) + .append(build_select_variant(query)) .nest(2), ); } - doc.group().append(build_semicolon(set.semicolon_token())) + if let Some(data) = view.data_option() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(data.syntax())) + .append(build_keyword_node(data.syntax())) + .nest(2), + ); + } + doc.group().append(build_semicolon(view.semicolon_token())) } -fn build_transaction_mode_list<'a>(list: ast::TransactionModeList) -> Doc<'a> { - let modes = list.transaction_modes().map(|mode| { - let syntax = mode.syntax().clone(); +fn build_explain_stmt<'a>(stmt: ast::ExplainStmt) -> Doc<'a> { + match stmt { + ast::ExplainStmt::CompoundSelect(stmt) => build_compound_select(&stmt), + ast::ExplainStmt::CreateTableAs(stmt) => build_create_table_as(&stmt), + ast::ExplainStmt::Declare(stmt) => build_declare(&stmt), + ast::ExplainStmt::Delete(stmt) => build_delete(&stmt), + ast::ExplainStmt::Execute(stmt) => build_execute(stmt), + ast::ExplainStmt::Insert(stmt) => build_insert(&stmt), + ast::ExplainStmt::Merge(stmt) => build_merge(&stmt), + ast::ExplainStmt::ParenSelect(stmt) => build_paren_select(stmt), + ast::ExplainStmt::Select(stmt) => build_select_doc(&stmt), + ast::ExplainStmt::SelectInto(stmt) => build_select_into(&stmt), + ast::ExplainStmt::Table(stmt) => build_table(&stmt), + ast::ExplainStmt::Update(stmt) => build_update(&stmt), + ast::ExplainStmt::Values(stmt) => build_values(&stmt), + ast::ExplainStmt::CreateMaterializedView(stmt) => build_create_materialized_view(&stmt), + } +} + +fn build_role_ref_list<'a>(list: ast::RoleRefList) -> Doc<'a> { + build_comma_separated_docs(list.role_refs().map(|role| { + let syntax = role.syntax().clone(); ( - leading_comments(&syntax).append(build_keyword_node(&syntax)), + leading_comments(&syntax).append(build_role_ref(&role)), syntax, ) - }); - build_comma_separated_docs(modes).unwrap_or_else(Doc::nil) + })) + .unwrap_or_else(Doc::nil) } -fn build_set_transaction<'a>(set: &ast::SetTransaction) -> Doc<'a> { - let mut doc = Doc::text("set"); - let body = if let Some(characteristics) = set.session_characteristics() { - let mut body = Doc::text("session"); - for (token, keyword) in [ - (characteristics.characteristics_token(), "characteristics"), - (characteristics.as_token(), "as"), - (characteristics.transaction_token(), "transaction"), - ] { - if let Some(token) = token { - body = body - .append(Doc::line_or_space()) - .append(leading_comments_token(&token)) - .append(Doc::text(keyword)); +fn build_keyword_tokens<'a, const N: usize>( + tokens: [(Option, &'static str); N], +) -> Doc<'a> { + let mut doc = Doc::nil(); + let mut has_keyword = false; + for (token, keyword) in tokens { + if let Some(token) = token { + if has_keyword { + doc = doc.append(Doc::space()); } + has_keyword = true; + doc = doc + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } - if let Some(modes) = characteristics.transaction_mode_list() { - body = body.append( - Doc::line_or_space() - .append(leading_comments(modes.syntax())) - .append(build_transaction_mode_list(modes)) - .nest(2), - ); - } - Some((leading_comments(characteristics.syntax()), body)) - } else if let Some(modes) = set.transaction_modes() { - let mut body = Doc::text("transaction"); - if let Some(list) = modes.transaction_mode_list() { - body = body.append( - Doc::line_or_space() - .append(leading_comments(list.syntax())) - .append(build_transaction_mode_list(list)) - .nest(2), - ); - } - Some((leading_comments(modes.syntax()), body)) + } + doc +} + +fn build_revoke_command<'a>(command: ast::RevokeCommand) -> Doc<'a> { + let mut doc = if let Some(role) = command.role_ref() { + build_role_ref(&role) + } else if command.ident_token().is_some() { + build_name(command.syntax()) } else { - set.transaction_snapshot().map(|snapshot| { - let mut body = Doc::text("transaction"); - if let Some(token) = snapshot.snapshot_token() { - body = body - .append(Doc::line_or_space()) - .append(leading_comments_token(&token)) - .append(Doc::text("snapshot")); - } - if let Some(literal) = snapshot.literal() { - body = body.append( - Doc::line_or_space() - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)) - .nest(2), - ); - } - (leading_comments(snapshot.syntax()), body) - }) + let tokens = [ + (command.all_token(), "all"), + (command.alter_token(), "alter"), + (command.create_token(), "create"), + (command.delete_token(), "delete"), + (command.execute_token(), "execute"), + (command.insert_token(), "insert"), + (command.references_token(), "references"), + (command.select_token(), "select"), + (command.system_token(), "system"), + (command.temp_token(), "temp"), + (command.temporary_token(), "temporary"), + (command.trigger_token(), "trigger"), + (command.truncate_token(), "truncate"), + (command.update_token(), "update"), + ]; + build_keyword_tokens(tokens) }; - if let Some((comments, body)) = body { - doc = doc.append(Doc::line_or_space().append(comments).append(body).nest(2)); + if let Some(columns) = command.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); } - doc.group().append(build_semicolon(set.semicolon_token())) + doc.group() } -fn build_show<'a>(show: &ast::Show) -> Doc<'a> { - let mut doc = Doc::text("show"); - if let Some(action) = show.show_action() { - let comments = leading_comments(action.syntax()); - let action_doc = match action { - ast::ShowAction::ConfigParameterRef(parameter) => parameter - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil), - ast::ShowAction::All(action) => build_keyword_node(action.syntax()), - ast::ShowAction::SessionAuthorization(action) => build_keyword_node(action.syntax()), - ast::ShowAction::TimeZone(action) => build_keyword_node(action.syntax()), - ast::ShowAction::TransactionIsolationLevel(action) => { - build_keyword_node(action.syntax()) +fn build_privileges<'a>(privileges: ast::Privileges) -> Doc<'a> { + match privileges { + ast::Privileges::AllPrivileges(all) => { + let mut doc = build_keyword_tokens([ + (all.all_token(), "all"), + (all.privileges_token(), "privileges"), + ]); + if let Some(columns) = all.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); } - }; - doc = doc.append( - Doc::line_or_space() - .append(comments) - .append(action_doc) - .nest(2), - ); + doc + } + ast::Privileges::RevokeCommandList(commands) => { + build_comma_separated_docs(commands.revoke_commands().map(|command| { + let syntax = command.syntax().clone(); + ( + leading_comments(&syntax).append(build_revoke_command(command)), + syntax, + ) + })) + .unwrap_or_else(Doc::nil) + } } - doc.group().append(build_semicolon(show.semicolon_token())) } -fn build_unlisten<'a>(unlisten: &ast::Unlisten) -> Doc<'a> { - let mut doc = Doc::text("unlisten"); - let target = if let Some(channel) = unlisten.channel_ref() { - Some(leading_comments(channel.syntax()).append(build_name(channel.syntax()))) - } else { - unlisten - .star_token() - .map(|star| leading_comments_token(&star).append(Doc::text("*"))) - }; - if let Some(target) = target { - doc = doc.append(Doc::line_or_space().append(target).nest(2)); - } - doc.group() - .append(build_semicolon(unlisten.semicolon_token())) +fn build_path_items<'a>(items: Vec<(SyntaxNode, ast::PathRef)>) -> Doc<'a> { + build_comma_separated_docs(items.into_iter().map(|(syntax, path)| { + ( + leading_comments(&syntax).append(build_path_ref(&path)), + syntax, + ) + })) + .unwrap_or_else(Doc::nil) } -fn build_set<'a>(set: &ast::Set) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(scope) = set.set_scope() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(scope.syntax())) - .append(build_keyword_node(scope.syntax())) - .nest(2), - ); - } - if let Some(target) = set.set_target() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(target.syntax())) - .append(build_set_target(target)) - .nest(2), - ); - } - doc.group().append(build_semicolon(set.semicolon_token())) +fn build_name_items<'a>(items: Vec) -> Doc<'a> { + build_comma_separated_docs(items.into_iter().map(|syntax| { + let doc = leading_comments(&syntax).append(build_name(&syntax)); + (doc, syntax) + })) + .unwrap_or_else(Doc::nil) } -fn build_set_target<'a>(target: ast::SetTarget) -> Doc<'a> { - match target { - ast::SetTarget::SetCatalog(target) => build_set_literal_target("catalog", target.literal()), - ast::SetTarget::SetSchemaValue(target) => { - build_set_literal_target("schema", target.literal()) - } - ast::SetTarget::SetConfig(target) => build_set_config(target), - ast::SetTarget::SetTimeZone(target) => build_set_time_zone(target), - ast::SetTarget::SetXmlOption(target) => build_set_xml_option(target), - } +fn append_privilege_items<'a>(prefix: Doc<'a>, items: Doc<'a>) -> Doc<'a> { + prefix + .append(Doc::line_or_space().append(items).nest(2)) + .group() } -fn build_set_literal_target<'a>(keyword: &'static str, literal: Option) -> Doc<'a> { - let mut doc = Doc::text(keyword); - if let Some(literal) = literal { +fn build_function_sig<'a>(sig: ast::FunctionSig) -> Doc<'a> { + let mut doc = sig + .function_name_ref() + .and_then(|name| name.path_ref()) + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil); + if let Some(params) = sig.param_list() { doc = doc - .append(Doc::space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); + .append(comments_before(params.syntax().clone())) + .append(build_function_param_list(params)); } - doc.group() + doc } -fn build_set_xml_option<'a>(target: ast::SetXmlOption) -> Doc<'a> { - let mut doc = Doc::text("xml"); - if let Some(option) = target.option_token() { +fn build_procedure_sig<'a>(sig: ast::ProcedureSig) -> Doc<'a> { + let mut doc = sig + .procedure_name_ref() + .and_then(|name| name.path_ref()) + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil); + if let Some(params) = sig.param_list() { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&option)) - .append(Doc::text("option")); + .append(comments_before(params.syntax().clone())) + .append(build_function_param_list(params)); } - if let Some(value) = target.xml_document_or_content() { + doc +} + +fn build_routine_sig<'a>(sig: ast::RoutineSig) -> Doc<'a> { + let mut doc = sig + .routine_name_ref() + .and_then(|name| name.path_ref()) + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil); + if let Some(params) = sig.param_list() { doc = doc - .append(Doc::space()) - .append(leading_comments(value.syntax())) - .append(build_keyword_node(value.syntax())); + .append(comments_before(params.syntax().clone())) + .append(build_function_param_list(params)); } - doc.group() + doc } -fn build_set_time_zone<'a>(target: ast::SetTimeZone) -> Doc<'a> { - let mut doc = Doc::text("time"); - if let Some(zone) = target.zone_token() { - doc = doc +fn build_privilege_objects<'a>(objects: ast::PrivilegeObjects) -> Doc<'a> { + match objects { + ast::PrivilegeObjects::PrivilegeAllFunctionsInSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.all_token(), "all"), + (node.functions_token(), "functions"), + (node.in_token(), "in"), + (node.schema_token(), "schema"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeAllProceduresInSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.all_token(), "all"), + (node.procedures_token(), "procedures"), + (node.in_token(), "in"), + (node.schema_token(), "schema"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeAllRoutinesInSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.all_token(), "all"), + (node.routines_token(), "routines"), + (node.in_token(), "in"), + (node.schema_token(), "schema"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeAllSequencesInSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.all_token(), "all"), + (node.sequences_token(), "sequences"), + (node.in_token(), "in"), + (node.schema_token(), "schema"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeAllTablesInSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.all_token(), "all"), + (node.tables_token(), "tables"), + (node.in_token(), "in"), + (node.schema_token(), "schema"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeDatabase(node) => { + let items = build_name_items( + node.database_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.database_token(), "database")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeDefault(node) => build_path_items( + node.relation_name_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ), + ast::PrivilegeObjects::PrivilegeDomain(node) => { + let items = build_path_items( + node.domain_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.domain_token(), "domain")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeForeignDataWrapper(node) => { + let items = build_name_items( + node.foreign_data_wrapper_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.foreign_token(), "foreign"), + (node.data_token(), "data"), + (node.wrapper_token(), "wrapper"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeForeignServer(node) => { + let items = build_name_items( + node.server_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.foreign_token(), "foreign"), + (node.server_token(), "server"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeLanguage(node) => { + let items = build_name_items( + node.language_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.language_token(), "language")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeParameter(node) => { + let items = + build_comma_separated_docs(node.config_parameter_refs().filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| { + ( + leading_comments(&syntax).append(build_path_ref(&path)), + syntax, + ) + }) + })) + .unwrap_or_else(Doc::nil); + append_privilege_items( + build_keyword_tokens([(node.parameter_token(), "parameter")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegePropertyGraph(node) => { + let items = build_path_items( + node.property_graph_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([ + (node.property_token(), "property"), + (node.graph_token(), "graph"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeSchema(node) => { + let items = build_name_items( + node.schema_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.schema_token(), "schema")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeSequence(node) => { + let items = build_path_items( + node.sequence_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.sequence_token(), "sequence")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeTable(node) => { + let items = build_path_items( + node.relation_name_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ); + append_privilege_items(build_keyword_tokens([(node.table_token(), "table")]), items) + } + ast::PrivilegeObjects::PrivilegeTablespace(node) => { + let items = build_name_items( + node.tablespace_refs() + .map(|item| item.syntax().clone()) + .collect(), + ); + append_privilege_items( + build_keyword_tokens([(node.tablespace_token(), "tablespace")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeType(node) => { + let items = build_path_items( + node.type_name_refs() + .filter_map(|item| { + let syntax = item.syntax().clone(); + item.path_ref().map(|path| (syntax, path)) + }) + .collect(), + ); + append_privilege_items(build_keyword_tokens([(node.type_token(), "type")]), items) + } + ast::PrivilegeObjects::PrivilegeLargeObject(node) => { + let items = build_comma_separated_docs(node.literals().map(|item| { + let syntax = item.syntax().clone(); + ( + leading_comments(&syntax).append(build_literal(item)), + syntax, + ) + })) + .unwrap_or_else(Doc::nil); + append_privilege_items( + build_keyword_tokens([ + (node.large_token(), "large"), + (node.object_token(), "object"), + ]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeFunction(node) => { + let items = node + .function_sig_list() + .and_then(|list| { + build_comma_separated_docs(list.function_sigs().map(|sig| { + let syntax = sig.syntax().clone(); + ( + leading_comments(&syntax).append(build_function_sig(sig)), + syntax, + ) + })) + }) + .unwrap_or_else(Doc::nil); + append_privilege_items( + build_keyword_tokens([(node.function_token(), "function")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeProcedure(node) => { + let items = node + .procedure_sig_list() + .and_then(|list| { + build_comma_separated_docs(list.procedure_sigs().map(|sig| { + let syntax = sig.syntax().clone(); + ( + leading_comments(&syntax).append(build_procedure_sig(sig)), + syntax, + ) + })) + }) + .unwrap_or_else(Doc::nil); + append_privilege_items( + build_keyword_tokens([(node.procedure_token(), "procedure")]), + items, + ) + } + ast::PrivilegeObjects::PrivilegeRoutine(node) => { + let items = node + .routine_sig_list() + .and_then(|list| { + build_comma_separated_docs(list.routine_sigs().map(|sig| { + let syntax = sig.syntax().clone(); + ( + leading_comments(&syntax).append(build_routine_sig(sig)), + syntax, + ) + })) + }) + .unwrap_or_else(Doc::nil); + append_privilege_items( + build_keyword_tokens([(node.routine_token(), "routine")]), + items, + ) + } + } +} + +fn build_grant_with_clause<'a>(with: ast::GrantWithClause) -> Doc<'a> { + let mut doc = Doc::text("with"); + if let Some(option) = with.grant_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_node(option.syntax())); + } else if let Some(options) = with.grant_role_option_list() { + let options_doc = build_comma_separated_docs(options.grant_role_options().map(|option| { + let syntax = option.syntax().clone(); + let mut option_doc = option + .grant_role_option_name() + .map(|name| build_keyword_node(name.syntax())) + .unwrap_or_else(Doc::nil); + let value = option + .option_token() + .map(|token| (token, "option")) + .or_else(|| option.true_token().map(|token| (token, "true"))) + .or_else(|| option.false_token().map(|token| (token, "false"))); + if let Some((token, keyword)) = value { + option_doc = option_doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + (leading_comments(&syntax).append(option_doc), syntax) + })) + .unwrap_or_else(Doc::nil); + doc = doc.append(Doc::line_or_space().append(options_doc).nest(2)); + } + doc.group() +} + +fn build_granted_by_clause<'a>(granted: ast::GrantedByClause) -> Doc<'a> { + let mut doc = Doc::text("granted"); + if let Some(by) = granted.by_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&by)) + .append(Doc::text("by")); + } + if let Some(role) = granted.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + doc.group() +} + +fn build_grant<'a>(grant: &ast::Grant) -> Doc<'a> { + let mut doc = Doc::text("grant"); + if let Some(privileges) = grant.privileges() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(privileges.syntax())) + .append(build_privileges(privileges)) + .nest(2), + ); + } + if let Some(on) = grant.on_privilege_objects_clause() { + let mut on_doc = Doc::text("on"); + if let Some(objects) = on.privilege_objects() { + on_doc = on_doc.append( + Doc::line_or_space() + .append(leading_comments(objects.syntax())) + .append(build_privilege_objects(objects)) + .nest(2), + ); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); + } + if let Some(to) = grant.to_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&to)) + .append(Doc::text("to")) + .nest(2), + ); + } + if let Some(roles) = grant.role_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + if let Some(with) = grant.grant_with_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(with.syntax())) + .append(build_grant_with_clause(with)) + .nest(2), + ); + } + if let Some(granted) = grant.granted_by_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(granted.syntax())) + .append(build_granted_by_clause(granted)) + .nest(2), + ); + } + doc.group().append(build_semicolon(grant.semicolon_token())) +} + +fn build_revoke_option_for<'a>(option: ast::RevokeOptionFor) -> Doc<'a> { + match option { + ast::RevokeOptionFor::AdminOptionFor(option) => build_keyword_node(option.syntax()), + ast::RevokeOptionFor::GrantOptionFor(option) => build_keyword_node(option.syntax()), + ast::RevokeOptionFor::InheritOptionFor(option) => build_keyword_node(option.syntax()), + ast::RevokeOptionFor::SetOptionFor(option) => build_keyword_node(option.syntax()), + } +} + +fn build_revoke<'a>(revoke: &ast::Revoke) -> Doc<'a> { + let mut doc = Doc::text("revoke"); + if let Some(option) = revoke.revoke_option_for() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(option.syntax())) + .append(build_revoke_option_for(option)) + .nest(2), + ); + } + if let Some(privileges) = revoke.privileges() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(privileges.syntax())) + .append(build_privileges(privileges)) + .nest(2), + ); + } + if let Some(on) = revoke.on_privilege_objects_clause() { + let mut on_doc = Doc::text("on"); + if let Some(objects) = on.privilege_objects() { + on_doc = on_doc.append( + Doc::line_or_space() + .append(leading_comments(objects.syntax())) + .append(build_privilege_objects(objects)) + .nest(2), + ); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); + } + if let Some(from) = revoke.from_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&from)) + .append(Doc::text("from")) + .nest(2), + ); + } + if let Some(roles) = revoke.role_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + if let Some(granted) = revoke.granted_by_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(granted.syntax())) + .append(build_granted_by_clause(granted)) + .nest(2), + ); + } + if let Some(behavior) = revoke.drop_behavior() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(revoke.semicolon_token())) +} + +fn build_import_table_filter<'a>(filter: ast::ImportTableFilter) -> Doc<'a> { + let (mut doc, names, l_paren, r_paren) = match filter { + ast::ImportTableFilter::ExceptTables(filter) => ( + Doc::text("except"), + filter + .remote_table_name_refs() + .map(|name| (name.syntax().clone(), build_name(name.syntax()))) + .collect::>(), + filter.l_paren_token(), + filter.r_paren_token(), + ), + ast::ImportTableFilter::LimitToTables(filter) => { + let mut doc = Doc::text("limit"); + if let Some(to) = filter.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + ( + doc, + filter + .remote_table_name_refs() + .map(|name| (name.syntax().clone(), build_name(name.syntax()))) + .collect::>(), + filter.l_paren_token(), + filter.r_paren_token(), + ) + } + }; + let mut body = build_comma_separated_docs( + names + .into_iter() + .map(|(syntax, name)| (leading_comments(&syntax).append(name), syntax)), + ) + .unwrap_or_else(Doc::nil); + if let Some(r_paren) = r_paren { + body = body.append(comments_before(r_paren)); + } + doc = doc + .append(Doc::space()) + .append(l_paren.map(comments_before).unwrap_or_else(Doc::nil)) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")); + doc.group() +} + +fn build_import_foreign_schema<'a>(import: &ast::ImportForeignSchema) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (import.import_token(), "import"), + (import.foreign_token(), "foreign"), + (import.schema_token(), "schema"), + ]); + if let Some(schema) = import.schema_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(schema.syntax())) + .append(build_name(schema.syntax())); + } + if let Some(filter) = import.import_table_filter() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(filter.syntax())) + .append(build_import_table_filter(filter)) + .nest(2), + ); + } + if let Some(from) = import.from_token() { + let mut from_doc = leading_comments_token(&from).append(Doc::text("from")); + if let Some(server) = import.server_clause() { + from_doc = from_doc + .append(Doc::space()) + .append(leading_comments(server.syntax())) + .append(build_server_clause(server)); + } + doc = doc.append(Doc::line_or_space().append(from_doc).nest(2)); + } else if let Some(server) = import.server_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(server.syntax())) + .append(build_server_clause(server)) + .nest(2), + ); + } + if let Some(into) = import.into_schema() { + let mut into_doc = Doc::text("into"); + if let Some(schema) = into.schema_ref() { + into_doc = into_doc + .append(Doc::space()) + .append(leading_comments(schema.syntax())) + .append(build_name(schema.syntax())); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(into.syntax())) + .append(into_doc) + .nest(2), + ); + } + if let Some(options) = import.alter_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_alter_option_list(&options)) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(import.semicolon_token())) +} + +fn build_listen<'a>(listen: &ast::Listen) -> Doc<'a> { + let mut doc = Doc::text("listen"); + if let Some(channel) = listen.channel() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(channel.syntax())) + .append(build_name(channel.syntax())) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(listen.semicolon_token())) +} + +fn build_move<'a>(move_stmt: &ast::Move) -> Doc<'a> { + let mut doc = Doc::text("move"); + if let Some(action) = move_stmt.cursor_action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_cursor_action(action)) + .nest(2), + ); + } + if let Some(token) = move_stmt.from_token().or_else(|| move_stmt.in_token()) { + let keyword = if token.kind() == SyntaxKind::FROM_KW { + "from" + } else { + "in" + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)) + .nest(2), + ); + } + if let Some(cursor) = move_stmt.cursor_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(cursor.syntax())) + .append(build_name(cursor.syntax())) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(move_stmt.semicolon_token())) +} + +fn build_notify<'a>(notify: &ast::Notify) -> Doc<'a> { + let mut doc = Doc::text("notify"); + if let Some(channel) = notify.channel_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(channel.syntax())) + .append(build_name(channel.syntax())); + } + if let Some(comma) = notify.comma_token() { + doc = doc.append(comments_before(comma)).append(Doc::text(",")); + } + if let Some(payload) = notify.literal() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(payload.syntax())) + .append(build_literal(payload)) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(notify.semicolon_token())) +} + +fn build_reassign<'a>(reassign: &ast::Reassign) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (reassign.reassign_token(), "reassign"), + (reassign.owned_token(), "owned"), + (reassign.by_token(), "by"), + ]); + if let Some(roles) = reassign.before() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + if let Some(to) = reassign.to_token() { + let mut to_doc = leading_comments_token(&to).append(Doc::text("to")); + if let Some(roles) = reassign.after() { + to_doc = to_doc + .append(Doc::space()) + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)); + } + doc = doc.append(Doc::line_or_space().append(to_doc).nest(2)); + } else if let Some(roles) = reassign.after() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(reassign.semicolon_token())) +} + +fn build_refresh<'a>(refresh: &ast::Refresh) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (refresh.refresh_token(), "refresh"), + (refresh.materialized_token(), "materialized"), + (refresh.view_token(), "view"), + (refresh.concurrently_token(), "concurrently"), + ]); + if let Some(view) = refresh.view_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&view)) + .nest(2), + ); + } + if let Some(data) = refresh.data_option() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(data.syntax())) + .append(build_keyword_node(data.syntax())) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(refresh.semicolon_token())) +} + +fn build_repack<'a>(repack: &ast::Repack) -> Doc<'a> { + let mut doc = Doc::text("repack"); + if let Some(options) = repack.option_item_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_option_item_list(options)) + .nest(2), + ); + } + if let Some(tables) = repack.table_and_columns_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(tables.syntax())) + .append(build_table_and_columns_list(tables)) + .nest(2), + ); + } + if let Some(using_index) = repack.using_index() { + let mut using_doc = Doc::text("using"); + if let Some(index) = using_index.index_token() { + using_doc = using_doc + .append(Doc::space()) + .append(leading_comments_token(&index)) + .append(Doc::text("index")); + } + if let Some(index) = using_index.index_ref() { + using_doc = using_doc + .append(Doc::space()) + .append(build_extension_path_value(&index)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using_index.syntax())) + .append(using_doc) + .nest(2), + ); + } + doc.group() + .append(build_semicolon(repack.semicolon_token())) +} + +fn build_reset_role<'a>(reset: &ast::ResetRole) -> Doc<'a> { + let mut doc = Doc::text("reset"); + if let Some(role) = reset.role_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&role)) + .append(Doc::text("role")); + } + doc.append(build_semicolon(reset.semicolon_token())) +} + +fn build_reset_session_auth<'a>(reset: &ast::ResetSessionAuth) -> Doc<'a> { + let mut doc = Doc::text("reset"); + for (token, keyword) in [ + (reset.session_token(), "session"), + (reset.authorization_token(), "authorization"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + doc.append(build_semicolon(reset.semicolon_token())) +} + +fn append_security_object_value<'a>(prefix: Doc<'a>, value: Doc<'a>) -> Doc<'a> { + prefix + .append(Doc::line_or_space().append(value).nest(2)) + .group() +} + +fn build_security_object_value<'a>(node: &impl AstNode, value: Doc<'a>) -> Doc<'a> { + leading_comments(node.syntax()).append(value) +} + +fn build_aggregate_sig<'a>(aggregate: ast::Aggregate) -> Doc<'a> { + let mut doc = aggregate + .path_ref() + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil); + if let Some(params) = aggregate.param_list() { + doc = doc + .append(leading_comments(params.syntax())) + .append(build_aggregate_param_list(params)); + } + doc +} + +fn build_security_label_object<'a>(object: ast::SecurityLabelObject) -> Doc<'a> { + match object { + ast::SecurityLabelObject::ObjectAggregate(node) => { + let value = node + .aggregate() + .map(|value| { + let doc = build_aggregate_sig(value.clone()); + build_security_object_value(&value, doc) + }) + .unwrap_or_else(Doc::nil); + append_security_object_value( + build_keyword_tokens([(node.aggregate_token(), "aggregate")]), + value, + ) + } + ast::SecurityLabelObject::ObjectColumn(node) => { + let value = node + .name() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil); + append_security_object_value( + build_keyword_tokens([(node.column_token(), "column")]), + value, + ) + } + ast::SecurityLabelObject::ObjectDatabase(node) => append_security_object_value( + build_keyword_tokens([(node.database_token(), "database")]), + node.database_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectDomain(node) => append_security_object_value( + build_keyword_tokens([(node.domain_token(), "domain")]), + node.domain_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectEventTrigger(node) => append_security_object_value( + build_keyword_tokens([ + (node.event_token(), "event"), + (node.trigger_token(), "trigger"), + ]), + node.event_trigger_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectForeignTable(node) => append_security_object_value( + build_keyword_tokens([ + (node.foreign_token(), "foreign"), + (node.table_token(), "table"), + ]), + node.table_name_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectFunction(node) => append_security_object_value( + build_keyword_tokens([(node.function_token(), "function")]), + node.function_sig() + .map(|value| { + let doc = build_function_sig(value.clone()); + build_security_object_value(&value, doc) + }) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectLanguage(node) => append_security_object_value( + build_keyword_tokens([ + (node.procedural_token(), "procedural"), + (node.language_token(), "language"), + ]), + node.language_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectLargeObject(node) => append_security_object_value( + build_keyword_tokens([ + (node.large_token(), "large"), + (node.object_token(), "object"), + ]), + node.literal() + .map(|value| { + let doc = build_literal(value.clone()); + build_security_object_value(&value, doc) + }) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectMaterializedView(node) => append_security_object_value( + build_keyword_tokens([ + (node.materialized_token(), "materialized"), + (node.view_token(), "view"), + ]), + node.view_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectProcedure(node) => append_security_object_value( + build_keyword_tokens([(node.procedure_token(), "procedure")]), + node.procedure_sig() + .map(|value| { + let doc = build_procedure_sig(value.clone()); + build_security_object_value(&value, doc) + }) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectPublication(node) => append_security_object_value( + build_keyword_tokens([(node.publication_token(), "publication")]), + node.publication_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectRole(node) => append_security_object_value( + build_keyword_tokens([(node.role_token(), "role")]), + node.role_ref() + .map(|role| { + let doc = build_role_ref(&role); + build_security_object_value(&role, doc) + }) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectRoutine(node) => append_security_object_value( + build_keyword_tokens([(node.routine_token(), "routine")]), + node.routine_sig() + .map(|value| { + let doc = build_routine_sig(value.clone()); + build_security_object_value(&value, doc) + }) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectSchema(node) => append_security_object_value( + build_keyword_tokens([(node.schema_token(), "schema")]), + node.schema_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectSequence(node) => append_security_object_value( + build_keyword_tokens([(node.sequence_token(), "sequence")]), + node.sequence_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectSubscription(node) => append_security_object_value( + build_keyword_tokens([(node.subscription_token(), "subscription")]), + node.subscription_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectTable(node) => append_security_object_value( + build_keyword_tokens([(node.table_token(), "table")]), + node.table_name_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectTablespace(node) => append_security_object_value( + build_keyword_tokens([(node.tablespace_token(), "tablespace")]), + node.tablespace_ref() + .map(|name| build_security_object_value(&name, build_name(name.syntax()))) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectType(node) => append_security_object_value( + build_keyword_tokens([(node.type_token(), "type")]), + node.type_name_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + ast::SecurityLabelObject::ObjectView(node) => append_security_object_value( + build_keyword_tokens([(node.view_token(), "view")]), + node.view_ref() + .map(|name| build_extension_path_value(&name)) + .unwrap_or_else(Doc::nil), + ), + } +} + +fn build_security_label<'a>(label: &ast::SecurityLabel) -> Doc<'a> { + let mut doc = Doc::text("security"); + if let Some(token) = label.label_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("label")); + } + if let Some(provider) = label.for_provider() { + let mut provider_doc = Doc::text("for"); + if let Some(name) = provider.security_label_provider() { + provider_doc = provider_doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } else if let Some(literal) = provider.literal() { + provider_doc = provider_doc + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(provider.syntax())) + .append(provider_doc) + .nest(2), + ); + } + let object = label.security_label_object(); + if let Some(on) = label.on_token() { + let mut on_doc = leading_comments_token(&on).append(Doc::text("on")); + if let Some(object) = object { + on_doc = on_doc + .append(Doc::space()) + .append(leading_comments(object.syntax())) + .append(build_security_label_object(object)); + } + doc = doc.append(Doc::line_or_space().append(on_doc).nest(2)); + } else if let Some(object) = object { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(object.syntax())) + .append(build_security_label_object(object)) + .nest(2), + ); + } + + let value = if let Some(literal) = label.literal() { + Some(leading_comments(literal.syntax()).append(build_literal(literal))) + } else { + label + .null_token() + .map(|null| leading_comments_token(&null).append(Doc::text("null"))) + }; + if let Some(is) = label.is_token() { + let mut is_doc = leading_comments_token(&is).append(Doc::text("is")); + if let Some(value) = value { + is_doc = is_doc.append(Doc::space()).append(value); + } + doc = doc.append(Doc::line_or_space().append(is_doc).nest(2)); + } else if let Some(value) = value { + doc = doc.append(Doc::line_or_space().append(value).nest(2)); + } + doc.group().append(build_semicolon(label.semicolon_token())) +} + +fn build_set_constraints<'a>(set: &ast::SetConstraints) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(constraints) = set.constraints_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&constraints)) + .append(Doc::text("constraints")) + .nest(2), + ); + } + + let names = set.constraint_name_refs().map(|name| { + let syntax = name.syntax().clone(); + (build_extension_path_value(&name), syntax) + }); + let target = if let Some(names) = build_comma_separated_docs(names) { + Some(names) + } else { + set.all_token() + .map(|all| leading_comments_token(&all).append(Doc::text("all"))) + }; + if let Some(target) = target { + doc = doc.append(Doc::line_or_space().append(target).nest(2)); + } + if let Some(timing) = set.constraint_timing() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(timing.syntax())) + .append(build_keyword_node(timing.syntax())) + .nest(2), + ); + } + doc.group().append(build_semicolon(set.semicolon_token())) +} + +fn build_role_ref<'a>(role: &ast::RoleRef) -> Doc<'a> { + if let Some(group) = role.group_token() { + let mut doc = leading_comments_token(&group).append(Doc::text("group")); + if let Some(ident) = role.ident_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&ident)) + .append(build_name(role.syntax())); + } + doc + } else if role.ident_token().is_some() { + build_name(role.syntax()) + } else { + build_keyword_node(role.syntax()) + } +} + +fn build_set_role_target<'a>(target: ast::SetRoleTarget) -> Doc<'a> { + match target { + ast::SetRoleTarget::Literal(literal) => build_literal(literal), + ast::SetRoleTarget::RoleRef(role) => build_role_ref(&role), + ast::SetRoleTarget::SetRoleNone(none) => build_keyword_node(none.syntax()), + } +} + +fn build_set_role<'a>(set: &ast::SetRole) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(scope) = set.set_scope() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(scope.syntax())) + .append(build_keyword_node(scope.syntax())) + .nest(2), + ); + } + if let Some(role) = set.role_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&role)) + .append(Doc::text("role")) + .nest(2), + ); + } + if let Some(target) = set.set_role_target() { + let comments = leading_comments(target.syntax()); + doc = doc.append( + Doc::line_or_space() + .append(comments) + .append(build_set_role_target(target)) + .nest(2), + ); + } + doc.group().append(build_semicolon(set.semicolon_token())) +} + +fn build_set_session_auth_target<'a>(target: ast::SetSessionAuthTarget) -> Doc<'a> { + match target { + ast::SetSessionAuthTarget::Literal(literal) => build_literal(literal), + ast::SetSessionAuthTarget::RoleRef(role) => build_role_ref(&role), + ast::SetSessionAuthTarget::SetSessionAuthDefault(default) => { + build_keyword_node(default.syntax()) + } + } +} + +fn build_set_session_auth<'a>(set: &ast::SetSessionAuth) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(scope) = set.set_scope() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(scope.syntax())) + .append(build_keyword_node(scope.syntax())) + .nest(2), + ); + } + if let Some(session) = set.session_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&session)) + .append(Doc::text("session")) + .nest(2), + ); + } + if let Some(authorization) = set.authorization_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&authorization)) + .append(Doc::text("authorization")) + .nest(2), + ); + } + if let Some(target) = set.set_session_auth_target() { + let comments = leading_comments(target.syntax()); + doc = doc.append( + Doc::line_or_space() + .append(comments) + .append(build_set_session_auth_target(target)) + .nest(2), + ); + } + doc.group().append(build_semicolon(set.semicolon_token())) +} + +fn build_transaction_mode_list<'a>(list: ast::TransactionModeList) -> Doc<'a> { + let modes = list.transaction_modes().map(|mode| { + let syntax = mode.syntax().clone(); + ( + leading_comments(&syntax).append(build_keyword_node(&syntax)), + syntax, + ) + }); + build_comma_separated_docs(modes).unwrap_or_else(Doc::nil) +} + +fn build_set_transaction<'a>(set: &ast::SetTransaction) -> Doc<'a> { + let mut doc = Doc::text("set"); + let body = if let Some(characteristics) = set.session_characteristics() { + let mut body = Doc::text("session"); + for (token, keyword) in [ + (characteristics.characteristics_token(), "characteristics"), + (characteristics.as_token(), "as"), + (characteristics.transaction_token(), "transaction"), + ] { + if let Some(token) = token { + body = body + .append(Doc::line_or_space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(modes) = characteristics.transaction_mode_list() { + body = body.append( + Doc::line_or_space() + .append(leading_comments(modes.syntax())) + .append(build_transaction_mode_list(modes)) + .nest(2), + ); + } + Some((leading_comments(characteristics.syntax()), body)) + } else if let Some(modes) = set.transaction_modes() { + let mut body = Doc::text("transaction"); + if let Some(list) = modes.transaction_mode_list() { + body = body.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(build_transaction_mode_list(list)) + .nest(2), + ); + } + Some((leading_comments(modes.syntax()), body)) + } else { + set.transaction_snapshot().map(|snapshot| { + let mut body = Doc::text("transaction"); + if let Some(token) = snapshot.snapshot_token() { + body = body + .append(Doc::line_or_space()) + .append(leading_comments_token(&token)) + .append(Doc::text("snapshot")); + } + if let Some(literal) = snapshot.literal() { + body = body.append( + Doc::line_or_space() + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)) + .nest(2), + ); + } + (leading_comments(snapshot.syntax()), body) + }) + }; + if let Some((comments, body)) = body { + doc = doc.append(Doc::line_or_space().append(comments).append(body).nest(2)); + } + doc.group().append(build_semicolon(set.semicolon_token())) +} + +fn build_show<'a>(show: &ast::Show) -> Doc<'a> { + let mut doc = Doc::text("show"); + if let Some(action) = show.show_action() { + let comments = leading_comments(action.syntax()); + let action_doc = match action { + ast::ShowAction::ConfigParameterRef(parameter) => parameter + .path_ref() + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil), + ast::ShowAction::All(action) => build_keyword_node(action.syntax()), + ast::ShowAction::SessionAuthorization(action) => build_keyword_node(action.syntax()), + ast::ShowAction::TimeZone(action) => build_keyword_node(action.syntax()), + ast::ShowAction::TransactionIsolationLevel(action) => { + build_keyword_node(action.syntax()) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(comments) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(show.semicolon_token())) +} + +fn build_unlisten<'a>(unlisten: &ast::Unlisten) -> Doc<'a> { + let mut doc = Doc::text("unlisten"); + let target = if let Some(channel) = unlisten.channel_ref() { + Some(leading_comments(channel.syntax()).append(build_name(channel.syntax()))) + } else { + unlisten + .star_token() + .map(|star| leading_comments_token(&star).append(Doc::text("*"))) + }; + if let Some(target) = target { + doc = doc.append(Doc::line_or_space().append(target).nest(2)); + } + doc.group() + .append(build_semicolon(unlisten.semicolon_token())) +} + +fn build_set<'a>(set: &ast::Set) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(scope) = set.set_scope() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(scope.syntax())) + .append(build_keyword_node(scope.syntax())) + .nest(2), + ); + } + if let Some(target) = set.set_target() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(target.syntax())) + .append(build_set_target(target)) + .nest(2), + ); + } + doc.group().append(build_semicolon(set.semicolon_token())) +} + +fn build_set_target<'a>(target: ast::SetTarget) -> Doc<'a> { + match target { + ast::SetTarget::SetCatalog(target) => build_set_literal_target("catalog", target.literal()), + ast::SetTarget::SetSchemaValue(target) => { + build_set_literal_target("schema", target.literal()) + } + ast::SetTarget::SetConfig(target) => build_set_config(target), + ast::SetTarget::SetTimeZone(target) => build_set_time_zone(target), + ast::SetTarget::SetXmlOption(target) => build_set_xml_option(target), + } +} + +fn build_set_literal_target<'a>(keyword: &'static str, literal: Option) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(literal) = literal { + doc = doc + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + doc.group() +} + +fn build_set_xml_option<'a>(target: ast::SetXmlOption) -> Doc<'a> { + let mut doc = Doc::text("xml"); + if let Some(option) = target.option_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&option)) + .append(Doc::text("option")); + } + if let Some(value) = target.xml_document_or_content() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_keyword_node(value.syntax())); + } + doc.group() +} + +fn build_set_time_zone<'a>(target: ast::SetTimeZone) -> Doc<'a> { + let mut doc = Doc::text("time"); + if let Some(zone) = target.zone_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&zone)) + .append(Doc::text("zone")); + } + let value = if let Some(value) = target.config_value() { + let value_doc = match value.clone() { + ast::ConfigValue::ConfigValueName(name) => build_name(name.syntax()), + ast::ConfigValue::Literal(literal) => build_literal(literal), + }; + Some(leading_comments(value.syntax()).append(value_doc)) + } else if let Some(default) = target.default_token() { + Some(leading_comments_token(&default).append(Doc::text("default"))) + } else { + target + .local_token() + .map(|local| leading_comments_token(&local).append(Doc::text("local"))) + }; + if let Some(value) = value { + doc = doc.append(Doc::space()).append(value); + } + doc.group() +} + +fn build_set_config<'a>(set: ast::SetConfig) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(parameter) = set.config_parameter_ref() { + doc = doc.append(leading_comments(parameter.syntax())); + if let Some(path) = parameter.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(assignment) = set.config_assignment() { + let comments = leading_comments(assignment.syntax()); + doc = doc + .append(Doc::line_or_space()) + .append(comments) + .append(build_config_assignment(assignment)); + } + doc.group() +} + +fn build_set_config_param<'a>(set: &ast::SetConfigParam) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(parameter) = set.config_parameter_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(parameter.syntax())); + if let Some(path) = parameter.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(assignment) = set.config_assignment() { + let comments = leading_comments(assignment.syntax()); + doc = doc + .append(Doc::space()) + .append(comments) + .append(build_config_assignment(assignment)); + } + doc.group() +} + +fn build_config_assignment<'a>(assignment: ast::ConfigAssignment) -> Doc<'a> { + match assignment { + ast::ConfigAssignment::FromCurrent(current) => build_keyword_node(current.syntax()), + ast::ConfigAssignment::ToConfigValue(values) => { + let mut doc = if let Some(eq) = values.eq_token() { + leading_comments_token(&eq).append(Doc::text("=")) + } else if let Some(to) = values.to_token() { + leading_comments_token(&to).append(Doc::text("to")) + } else { + Doc::nil() + }; + let value_docs = values.config_values().map(|value| { + let syntax = value.syntax().clone(); + let value_doc = match value { + ast::ConfigValue::ConfigValueName(name) => build_name(name.syntax()), + ast::ConfigValue::Literal(literal) => build_literal(literal), + }; + (leading_comments(&syntax).append(value_doc), syntax) + }); + if let Some(values_doc) = build_comma_separated_docs(value_docs) { + doc = doc.append(Doc::space()).append(values_doc); + } else if let Some(default) = values.default_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } else if let Some(null) = values.null_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&null)) + .append(Doc::text("null")); + } + doc.group() + } + } +} + +fn build_create_index<'a>(create_index: &ast::CreateIndex) -> Doc<'a> { + let mut doc = Doc::text("create"); + + if let Some(unique_token) = create_index.unique_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&unique_token)) + .append(Doc::text("unique")); + } + if let Some(index_token) = create_index.index_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&index_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("index")); + if let Some(concurrently_token) = create_index.concurrently_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&concurrently_token)) + .append(Doc::text("concurrently")); + } + if let Some(if_not_exists) = create_index.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + if let Some(index) = create_index.index() { + doc = doc + .append(Doc::space()) + .append(leading_comments(index.syntax())); + if let Some(path) = index.path() { + doc = doc.append(build_path(&path)); + } + } + let mut on_doc = Doc::nil(); + if let Some(on_token) = create_index.on_token() { + on_doc = on_doc + .append(leading_comments_token(&on_token)) + .append(Doc::text("on")); + } + if let Some(table) = create_index.table_relation_name() { + on_doc = on_doc + .append(Doc::space()) + .append(build_table_relation_name(table)); + } + if let Some(using_method) = create_index.using_method() { + let mut using_doc = + leading_comments(using_method.syntax()).append(build_using_method(using_method)); + if let Some(items) = create_index.partition_item_list() { + using_doc = using_doc + .append(Doc::space()) + .append(leading_comments(items.syntax())) + .append(build_create_table_partition_items(items)); + } + on_doc = on_doc.append(Doc::line_or_space()).append(using_doc); + doc = doc.append(Doc::hard_line().append(on_doc.group()).nest(2)); + } else { + if let Some(items) = create_index.partition_item_list() { + on_doc = on_doc + .append(Doc::space()) + .append(leading_comments(items.syntax())) + .append(build_create_table_partition_items(items)); + } + doc = doc.append(Doc::line_or_space().append(on_doc).nest(2)); + } + if let Some(include) = create_index.index_include_clause() { + let mut include_doc = leading_comments(include.syntax()).append(Doc::text("include")); + if let Some(items) = include.partition_item_list() { + include_doc = include_doc + .append(Doc::space()) + .append(leading_comments(items.syntax())) + .append(build_create_table_partition_items(items)); + } + doc = doc.append(Doc::line_or_space().append(include_doc).nest(2)); + } + if let Some(nulls) = create_index.nulls_distinct_option() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(nulls.syntax())) + .append(build_keyword_node(nulls.syntax())) + .nest(2), + ); + } + if let Some(params) = create_index.with_params() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(params.syntax())) + .append(build_with_params(params)) + .nest(2), + ); + } + if let Some(tablespace) = create_index.tablespace_clause() { + let mut tablespace_doc = + leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); + if let Some(tablespace_ref) = tablespace.tablespace_ref() { + tablespace_doc = tablespace_doc + .append(Doc::space()) + .append(leading_comments(tablespace_ref.syntax())) + .append(build_name(tablespace_ref.syntax())); + } + doc = doc.append(Doc::line_or_space().append(tablespace_doc).nest(2)); + } + if let Some(where_clause) = create_index.where_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(where_clause.syntax())) + .append(build_where_clause(where_clause)) + .nest(2), + ); + } + + doc.group() + .append(build_semicolon(create_index.semicolon_token())) +} + +fn build_create_view<'a>(create_view: &ast::CreateView) -> Doc<'a> { + let mut doc = Doc::text("create"); + + if let Some(or_replace) = create_view.or_replace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(or_replace.syntax())) + .append(build_keyword_node(or_replace.syntax())); + } + if let Some(persistence) = create_view.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_keyword_node(persistence.syntax())); + } + if let Some(recursive) = create_view.recursive_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&recursive)) + .append(Doc::text("recursive")); + } + if let Some(view_token) = create_view.view_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&view_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("view")); + + if let Some(view) = create_view.view() { + doc = doc + .append(Doc::space()) + .append(leading_comments(view.syntax())); + if let Some(path) = view.path() { + doc = doc.append(build_path(&path)); + } + } + if let Some(columns) = create_view.column_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_cte_column_list(columns)); + } + let has_with_params = if let Some(with_params) = create_view.with_params() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(with_params.syntax())) + .append(build_with_params(with_params)) + .nest(2), + ); + true + } else { + false + }; + if let Some(as_token) = create_view.as_token() { + let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); + doc = if has_with_params { + doc.append(Doc::hard_line().append(as_doc).nest(2)) + } else { + doc.append(Doc::space()).append(as_doc) + }; + } + if let Some(query) = create_view.query() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(query.syntax())) + .append(build_select_variant(query)) + .nest(2), + ); + } + if let Some(check_option) = create_view.with_check_option() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(check_option.syntax())) + .append(build_with_check_option(check_option)) + .nest(2), + ); + } + + doc.append(build_semicolon(create_view.semicolon_token())) + .group() +} + +fn build_create_table_as<'a>(create_table_as: &ast::CreateTableAs) -> Doc<'a> { + let mut doc = Doc::text("create"); + + if let Some(persistence) = create_table_as.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_keyword_node(persistence.syntax())); + } + if let Some(table_token) = create_table_as.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&table_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("table")); + + if let Some(if_not_exists) = create_table_as.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + if let Some(table_name) = create_table_as.table_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table_name.syntax())); + if let Some(path) = table_name.path() { + doc = doc.append(build_path(&path)); + } + } + if let Some(arg_list) = create_table_as.table_arg_list() { + let comments = comments_before(arg_list.syntax().clone()); + if comment_tokens_before(arg_list.syntax().clone()).is_empty() { + doc = doc.append(Doc::space()); + } else { + doc = doc.append(comments).append(Doc::space()); + } + let body = Doc::list( + Itertools::intersperse( + arg_list.args().map(build_table_arg), + Doc::text(",").append(Doc::hard_line()), + ) + .collect(), + ); + doc = doc + .append(Doc::text("(")) + .append(wrap_body(body).group()) + .append(Doc::text(")")); + } + + let has_table_option = create_table_as.using_method().is_some() + || create_table_as.table_params().is_some() + || create_table_as.on_commit().is_some() + || create_table_as.tablespace_clause().is_some(); + if let Some(using_method) = create_table_as.using_method() { + let option = + leading_comments(using_method.syntax()).append(build_using_method(using_method)); + doc = doc.append(Doc::line_or_space().append(option).nest(2)); + } + if let Some(params) = create_table_as.table_params() { + let option = match params { + ast::TableParams::WithParams(params) => { + leading_comments(params.syntax()).append(build_with_params(params)) + } + ast::TableParams::WithoutOids(without_oids) => leading_comments(without_oids.syntax()) + .append(build_keyword_node(without_oids.syntax())), + }; + doc = doc.append(Doc::line_or_space().append(option).nest(2)); + } + if let Some(on_commit) = create_table_as.on_commit() { + let mut option = leading_comments(on_commit.syntax()).append(Doc::text("on")); + if let Some(commit_token) = on_commit.commit_token() { + option = option + .append(Doc::space()) + .append(leading_comments_token(&commit_token)); + } else { + option = option.append(Doc::space()); + } + option = option.append(Doc::text("commit")); + if let Some(action) = on_commit.on_commit_action() { + option = option + .append(Doc::space()) + .append(leading_comments(action.syntax())) + .append(build_keyword_node(action.syntax())); + } + doc = doc.append(Doc::line_or_space().append(option).nest(2)); + } + if let Some(tablespace) = create_table_as.tablespace_clause() { + let mut option = leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); + if let Some(name) = tablespace.tablespace_ref() { + option = option + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc = doc.append(Doc::line_or_space().append(option).nest(2)); + } + + if let Some(as_token) = create_table_as.as_token() { + let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); + doc = if has_table_option { + doc.append(Doc::hard_line().append(as_doc).nest(2)) + } else { + doc.append(Doc::space()).append(as_doc) + }; + } + if let Some(query) = create_table_as.query() { + let query_comments = leading_comments(query.syntax()); + let query_doc = match query { + ast::CreateTableAsQuery::SelectVariant(select) => build_select_variant(select), + ast::CreateTableAsQuery::Execute(execute) => build_execute(execute), + }; + doc = doc.append( + Doc::hard_line() + .append(query_comments) + .append(query_doc) + .nest(2), + ); + } + if let Some(data_option) = create_table_as.data_option() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(data_option.syntax())) + .append(build_keyword_node(data_option.syntax())) + .nest(2), + ); + } + + doc.append(build_semicolon(create_table_as.semicolon_token())) + .group() +} + +fn build_execute<'a>(execute: ast::Execute) -> Doc<'a> { + let mut doc = Doc::text("execute"); + if let Some(statement) = execute.prepared_statement_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(statement.syntax())) + .append(build_name(statement.syntax())); + } + if let Some(args) = execute.arg_list() { + doc = doc + .append(comments_before(args.syntax().clone())) + .append(build_call_arg_list(args)); + } + doc.group() + .append(build_semicolon(execute.semicolon_token())) +} + +fn build_with_check_option<'a>(check_option: ast::WithCheckOption) -> Doc<'a> { + let mut doc = Doc::text("with"); + if let Some(level) = check_option.check_option_level() { + let syntax = match &level { + ast::CheckOptionLevel::CascadedCheckOption(level) => level.syntax(), + ast::CheckOptionLevel::LocalCheckOption(level) => level.syntax(), + }; + doc = doc + .append(Doc::space()) + .append(leading_comments(syntax)) + .append(build_keyword_node(syntax)); + } + if let Some(check_token) = check_option.check_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&check_token)) + .append(Doc::text("check")); + } + if let Some(option_token) = check_option.option_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&option_token)) + .append(Doc::text("option")); + } + doc +} + +fn build_create_foreign_table<'a>(create_table: &ast::CreateForeignTable) -> Doc<'a> { + let mut doc = Doc::text("create"); + + if let Some(foreign) = create_table.foreign_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&foreign)) + .append(Doc::text("foreign")); + } + if let Some(table) = create_table.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&table)) + .append(Doc::text("table")); + } + if let Some(if_not_exists) = create_table.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + if let Some(table_name) = create_table.table_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table_name.syntax())); + if let Some(path) = table_name.path() { + doc = doc.append(build_path(&path)); + } + } + + if let Some(partition_of) = create_table.partition_of() { + let mut partition_doc = leading_comments(partition_of.syntax()) + .append(build_create_table_partition_of(partition_of)); + if let Some(arg_list) = create_table.table_arg_list() { + partition_doc = append_table_arg_list(partition_doc, arg_list); + } + if let Some(partition_type) = create_table.partition_type() { + let separator = if matches!(&partition_type, ast::PartitionType::PartitionDefault(_)) { + Doc::space() + } else { + Doc::line_or_space() + }; + partition_doc = partition_doc + .append(separator) + .append(leading_comments(partition_type.syntax())) + .append(build_create_table_partition_type(partition_type)); + } + doc = doc.append(Doc::hard_line().append(partition_doc).nest(2)); + } else { + if let Some(arg_list) = create_table.table_arg_list() { + doc = append_table_arg_list(doc, arg_list); + } + if let Some(inherits) = create_table.inherits() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(inherits.syntax())) + .append(build_create_table_inherits(inherits)) + .nest(2), + ); + } + if let Some(partition_type) = create_table.partition_type() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(partition_type.syntax())) + .append(build_create_table_partition_type(partition_type)) + .nest(2), + ); + } + } + if let Some(server) = create_table.server_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(server.syntax())) + .append(build_server_clause(server)) + .nest(2), + ); + } + if let Some(options) = create_table.alter_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_alter_option_list(&options)) + .nest(2), + ); + } + + doc.group() + .append(build_semicolon(create_table.semicolon_token())) +} + +fn append_table_arg_list<'a>(mut doc: Doc<'a>, arg_list: ast::TableArgList) -> Doc<'a> { + let has_leading_comments = !comment_tokens_before(arg_list.syntax().clone()).is_empty(); + if has_leading_comments { + doc = doc + .append(Doc::space()) + .append(leading_comments(arg_list.syntax())); + } + if let Some(l_paren) = arg_list.l_paren_token() { + if !comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(space_before_l_paren(l_paren)); + } else if !has_leading_comments { + doc = doc.append(Doc::space()); + } + } + let body = Doc::list( + Itertools::intersperse( + arg_list.args().map(build_table_arg), + Doc::text(",").append(Doc::hard_line()), + ) + .collect(), + ); + doc.append(Doc::text("(")) + .append(wrap_body(body).group()) + .append(Doc::text(")")) +} + +fn build_server_clause<'a>(server: ast::ServerClause) -> Doc<'a> { + let mut doc = Doc::text("server"); + if let Some(server_ref) = server.server_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(server_ref.syntax())) + .append(build_name(server_ref.syntax())); + } + doc +} + +fn build_create_table<'a>(create_table: &ast::CreateTable) -> Doc<'a> { + let mut doc = Doc::text("create"); + + if let Some(persistence) = create_table.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_keyword_node(persistence.syntax())); + } + + if let Some(table_token) = create_table.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&table_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("table")); + + if let Some(if_not_exists) = create_table.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + + if let Some(table_name) = create_table.table_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table_name.syntax())); + if let Some(path) = table_name.path() { + doc = doc.append(build_path(&path)); + } + } + + if let Some(partition_of) = create_table.partition_of() { + doc = doc.append( + Doc::hard_line() + .append(leading_comments(partition_of.syntax())) + .append(build_create_table_partition_of(partition_of)) + .nest(2), + ); + } + + if let Some(of_type) = create_table.of_type() { + let mut of_type_doc = leading_comments(of_type.syntax()).append(Doc::text("of")); + if let Some(ty) = of_type.ty() { + of_type_doc = of_type_doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + doc = doc.append(Doc::hard_line().append(of_type_doc).nest(2)); + } + + if let Some(arg_list) = create_table.table_arg_list() { + if let Some(l_paren) = arg_list.l_paren_token() { + if comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(Doc::space()); + } else { + doc = doc.append(comments_before(l_paren)); + } + } + let body = Doc::list( + Itertools::intersperse( + arg_list.args().map(build_table_arg), + Doc::text(",").append(Doc::hard_line()), + ) + .collect(), + ); + doc = doc + .append(Doc::text("(")) + .append(wrap_body(body).group()) + .append(Doc::text(")")); + } + + if let Some(partition_type) = create_table.partition_type() { + let separator = if matches!(&partition_type, ast::PartitionType::PartitionDefault(_)) { + Doc::space() + } else { + Doc::line_or_space() + }; + doc = doc.append( + separator + .append(leading_comments(partition_type.syntax())) + .append(build_create_table_partition_type(partition_type)) + .nest(2), + ); + } + + if let Some(inherits) = create_table.inherits() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(inherits.syntax())) + .append(build_create_table_inherits(inherits)) + .nest(2), + ); + } + + if let Some(partition_by) = create_table.partition_by() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(partition_by.syntax())) + .append(build_create_table_partition_by(partition_by)) + .nest(2), + ); + } + + if let Some(using_method) = create_table.using_method() { + let using_doc = + leading_comments(using_method.syntax()).append(build_using_method(using_method)); + doc = doc.append(Doc::line_or_space().append(using_doc).nest(2)); + } + + if let Some(params) = create_table.table_params() { + let (separator, params_doc) = match params { + ast::TableParams::WithParams(params) => ( + Doc::line_or_space(), + leading_comments(params.syntax()).append(build_with_params(params)), + ), + ast::TableParams::WithoutOids(without_oids) => ( + Doc::hard_line(), + leading_comments(without_oids.syntax()) + .append(build_keyword_node(without_oids.syntax())), + ), + }; + doc = doc.append(separator.append(params_doc).nest(2)); + } + + if let Some(on_commit) = create_table.on_commit() { + let mut on_commit_doc = leading_comments(on_commit.syntax()).append(Doc::text("on")); + if let Some(commit_token) = on_commit.commit_token() { + on_commit_doc = on_commit_doc + .append(Doc::space()) + .append(leading_comments_token(&commit_token)); + } else { + on_commit_doc = on_commit_doc.append(Doc::space()); + } + on_commit_doc = on_commit_doc.append(Doc::text("commit")); + if let Some(action) = on_commit.on_commit_action() { + on_commit_doc = on_commit_doc + .append(Doc::space()) + .append(leading_comments(action.syntax())) + .append(build_keyword_node(action.syntax())); + } + doc = doc.append(Doc::hard_line().append(on_commit_doc).nest(2)); + } + + if let Some(tablespace) = create_table.tablespace_clause() { + let mut tablespace_doc = + leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); + if let Some(tablespace_ref) = tablespace.tablespace_ref() { + tablespace_doc = tablespace_doc + .append(Doc::space()) + .append(leading_comments(tablespace_ref.syntax())) + .append(build_name(tablespace_ref.syntax())); + } + doc = doc.append(Doc::line_or_space().append(tablespace_doc).nest(2)); + } + + doc.group() + .append(build_semicolon(create_table.semicolon_token())) +} + +fn build_create_table_partition_of<'a>(partition_of: ast::PartitionOf) -> Doc<'a> { + let mut doc = Doc::text("partition"); + if let Some(of_token) = partition_of.of_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&of_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("of")); + if let Some(table) = partition_of.table_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table.syntax())); + if let Some(path) = table.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc +} + +fn build_create_table_inherits<'a>(inherits: ast::Inherits) -> Doc<'a> { + let mut doc = Doc::text("inherits"); + if let Some(l_paren) = inherits.l_paren_token() { + if comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(Doc::space()); + } else { + doc = doc.append(comments_before(l_paren)); + } + } + let tables = inherits.table_name_refs().map(|table| { + let mut item = leading_comments(table.syntax()); + if let Some(path) = table.path_ref() { + item = item.append(build_path_ref(&path)); + } + (item, table.syntax().clone()) + }); + let mut body = build_comma_separated_docs(tables).unwrap_or_else(Doc::nil); + if let Some(r_paren) = inherits.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() +} + +fn build_create_table_partition_by<'a>(partition_by: ast::PartitionBy) -> Doc<'a> { + let mut doc = Doc::text("partition"); + if let Some(by_token) = partition_by.by_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&by_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("by")); + if let Some(strategy) = partition_by.partition_strategy() { + doc = doc + .append(Doc::space()) + .append(leading_comments(strategy.syntax())) + .append(build_keyword_node(strategy.syntax())); + } + if let Some(items) = partition_by.partition_item_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(items.syntax())) + .append(build_create_table_partition_items(items)); + } + doc +} + +fn build_create_table_partition_items<'a>(items: ast::PartitionItemList) -> Doc<'a> { + let doc = items + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let item_docs = items.partition_items().map(|item| { + let mut item_doc = leading_comments(item.syntax()); + if let Some(expr) = item.expr() { + item_doc = item_doc.append(build_expr(expr)); + } + if item.expr().is_none() { + if let Some(collate) = item.collate() { + item_doc = item_doc.append(build_collate_expr(collate)); + } + } + if let Some(op_class) = item.op_class_ref() { + item_doc = item_doc + .append(Doc::space()) + .append(leading_comments(op_class.syntax())); + if let Some(path) = op_class.path_ref() { + item_doc = item_doc.append(build_path_ref(&path)); + } + } + if let Some(attributes) = item.attribute_list() { + item_doc = item_doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + if let Some(order) = item.sort_order() { + item_doc = item_doc + .append(Doc::space()) + .append(leading_comments(order.syntax())) + .append(build_keyword_node(order.syntax())); + } + if let Some(nulls) = item.nulls_order() { + item_doc = item_doc + .append(Doc::space()) + .append(leading_comments(nulls.syntax())) + .append(build_keyword_node(nulls.syntax())); + } + (item_doc, item.syntax().clone()) + }); + let mut body = build_comma_separated_docs(item_docs).unwrap_or_else(Doc::nil); + if let Some(r_paren) = items.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_create_table_partition_type<'a>(partition_type: ast::PartitionType) -> Doc<'a> { + match partition_type { + ast::PartitionType::PartitionDefault(_) => Doc::text("default"), + ast::PartitionType::PartitionForValuesIn(values) => { + let mut doc = build_partition_for_values_prefix(values.values_token()); + if let Some(in_token) = values.in_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&in_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("in")); + if let Some(l_paren) = values.l_paren_token() { + if comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(Doc::space()); + } else { + doc = doc.append(comments_before(l_paren)); + } + } + let body = build_comma_separated_exprs(values.exprs()).unwrap_or_else(Doc::nil); + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() + } + ast::PartitionType::PartitionForValuesFrom(values) => { + let mut doc = build_partition_for_values_prefix(values.values_token()); + if let Some(from_token) = values.from_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&from_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("from")); + if let Some(from) = values.from() { + doc = doc + .append(Doc::space()) + .append(leading_comments(from.syntax())) + .append(build_create_table_partition_values( + from.l_paren_token(), + from.exprs(), + from.r_paren_token(), + )); + } + doc = doc.append(Doc::line_or_space()); + if let Some(to_token) = values.to_token() { + doc = doc.append(leading_comments_token(&to_token)); + } + doc = doc.append(Doc::text("to")); + if let Some(to) = values.to() { + doc = doc + .append(Doc::space()) + .append(leading_comments(to.syntax())) + .append(build_create_table_partition_values( + to.l_paren_token(), + to.exprs(), + to.r_paren_token(), + )); + } + doc.group() + } + ast::PartitionType::PartitionForValuesWith(values) => { + let mut doc = build_partition_for_values_prefix(values.values_token()); + if let Some(with_token) = values.with_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&with_token)); + } else { + doc = doc.append(Doc::space()); + } + doc = doc.append(Doc::text("with")); + if let Some(l_paren) = values.l_paren_token() { + if comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(Doc::space()); + } else { + doc = doc.append(comments_before(l_paren)); + } + } + let mut parts = Vec::new(); + if let Some(modulus) = values.modulus() { + parts.push( + leading_comments(modulus.syntax()) + .append(build_keyword_node(modulus.syntax())) + .append(trailing_comments(modulus.syntax())), + ); + } + if let Some(remainder) = values.remainder() { + parts.push( + leading_comments(remainder.syntax()) + .append(build_keyword_node(remainder.syntax())) + .append(trailing_comments(remainder.syntax())), + ); + } + let body = Doc::list( + Itertools::intersperse( + parts.into_iter(), + Doc::text(",").append(Doc::line_or_space()), + ) + .collect(), + ); + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() + } + } +} + +fn build_partition_for_values_prefix<'a>(values_token: Option) -> Doc<'a> { + let mut doc = Doc::text("for"); + if let Some(values_token) = values_token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&values_token)); + } else { + doc = doc.append(Doc::space()); + } + doc.append(Doc::text("values")) +} + +fn build_create_table_partition_values<'a>( + l_paren: Option, + exprs: impl Iterator, + _r_paren: Option, +) -> Doc<'a> { + let doc = l_paren + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let body = build_comma_separated_exprs(exprs).unwrap_or_else(Doc::nil); + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_path<'a>(path: &ast::Path) -> Doc<'a> { + build_path_parts(path.qualifier(), path.dot_token(), path.segment()) +} + +fn build_path_ref<'a>(path: &ast::PathRef) -> Doc<'a> { + build_path_parts(path.qualifier(), path.dot_token(), path.segment()) +} + +fn build_path_parts<'a>( + qualifier: Option, + dot: Option, + segment: Option, +) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(qualifier) = qualifier { + doc = doc + .append(build_path_ref(&qualifier)) + .append(trailing_comments(qualifier.syntax())); + } + if dot.is_some() { + doc = doc.append(Doc::text(".")); + } + if let Some(segment) = segment { + doc = doc + .append(leading_comments(segment.syntax())) + .append(build_name(segment.syntax())); + } + doc +} + +fn build_name<'a>(node: &SyntaxNode) -> Doc<'a> { + let mut tokens = node + .children_with_tokens() + .filter_map(|el| el.into_token()) + .filter(|token| token.kind() != SyntaxKind::WHITESPACE); + + let Some(ident) = tokens.next() else { + return Doc::nil(); + }; + + if is_unicode_escape(ident.text()) { + let mut doc = Doc::text(ident.text().to_string()); + for token in tokens { + let text = match token.kind() { + SyntaxKind::STRING | SyntaxKind::COMMENT => token.text().to_string(), + _ => token.text().to_ascii_lowercase(), + }; + doc = doc.append(Doc::space()).append(Doc::text(text)); + if is_line_comment(&token) { + doc = doc.append(Doc::hard_line()); + } + } + return doc; + } + + Doc::text(quote_ident(&normalize_name_node(node))) +} + +fn is_unicode_escape(text: &str) -> bool { + text.strip_prefix(['u', 'U']) + .is_some_and(|text| text.starts_with("&\"")) +} + +fn build_table_arg<'a>(arg: ast::TableArg) -> Doc<'a> { + let doc = leading_comments(arg.syntax()); + let doc = doc.append(match &arg { + ast::TableArg::Column(column) => build_column(column), + ast::TableArg::LikeClause(like_clause) => build_like_clause(like_clause), + ast::TableArg::TableConstraint(table_constraint) => { + build_table_constraint(table_constraint.clone()) + } + }); + doc.append(trailing_comments(arg.syntax())) +} + +fn build_column<'a>(column: &ast::Column) -> Doc<'a> { + let mut doc = column + .name() + .map(|name| build_name(name.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(ty) = column.ty() { + doc = doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + if let Some(storage) = column.storage() { + let mut clause = Doc::text("storage"); + if let Some(mode) = storage.storage_mode() { + clause = clause + .append(Doc::space()) + .append(leading_comments(mode.syntax())) + .append(build_keyword_node(mode.syntax())); + } + doc = append_column_clause(doc, storage.syntax(), clause); + } + if let Some(compression) = column.compression_method() { + let mut clause = Doc::text("compression"); + if let Some(method) = compression.compression_method_name() { + clause = clause + .append(Doc::space()) + .append(leading_comments(method.syntax())) + .append(build_keyword_node(method.syntax())); + } + doc = append_column_clause(doc, compression.syntax(), clause); + } + if let Some(options) = column.with_options() { + doc = append_column_clause(doc, options.syntax(), build_keyword_node(options.syntax())); + } + if let Some(options) = column.alter_option_list() { + let syntax = options.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_alter_option_list(&options)); + } + if let Some(collate) = column.collate() { + let syntax = collate.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_collate_expr(collate)); + } + for constraint in column.constraints() { + let syntax = constraint.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_column_constraint(constraint)); + } + doc.group() +} + +fn append_column_clause<'a>(doc: Doc<'a>, syntax: &SyntaxNode, clause: Doc<'a>) -> Doc<'a> { + doc.append( + Doc::line_or_space() + .append(leading_comments(syntax)) + .append(clause) + .nest(2), + ) +} + +fn build_alter_option_list<'a>(list: &ast::AlterOptionList) -> Doc<'a> { + let mut doc = Doc::text("options"); + if let Some(l_paren) = list.l_paren_token() { + doc = doc.append(space_before_l_paren(l_paren)); + } else { + doc = doc.append(Doc::space()); + } + let items = list.alter_options().map(|option| { + let item = leading_comments(option.syntax()).append(build_alter_option(&option)); + (item, option.syntax().clone()) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = list.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(Doc::text("(")) + .append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) +} + +fn build_alter_option<'a>(option: &ast::AlterOption) -> Doc<'a> { + match option { + ast::AlterOption::AddForeignOption(option) => { + let mut doc = option + .add_token() + .map(|token| leading_comments_token(&token).append(Doc::text("add"))) + .unwrap_or_else(Doc::nil); + if let Some(name) = option.foreign_option_name() { + if option.add_token().is_some() { + doc = doc.append(Doc::space()); + } + doc = doc + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(value) = option.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_literal(value)); + } + doc + } + ast::AlterOption::SetForeignOption(option) => { + let mut doc = Doc::text("set"); + if let Some(name) = option.foreign_option_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(value) = option.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_literal(value)); + } + doc + } + ast::AlterOption::DropForeignOption(option) => { + let mut doc = Doc::text("drop"); + if let Some(name) = option.foreign_option_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc + } + } +} + +fn build_column_constraint<'a>(constraint: ast::ColumnConstraint) -> Doc<'a> { + match constraint { + ast::ColumnConstraint::CheckConstraint(constraint) => build_check_constraint(constraint), + ast::ColumnConstraint::DefaultConstraint(constraint) => { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(default) = constraint.default_token() { + doc = doc + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } + if let Some(expr) = constraint.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + append_constraint_options(doc, constraint.constraint_options()) + .nest(2) + .group() + } + ast::ColumnConstraint::ExcludeConstraint(constraint) => { + build_exclude_constraint(constraint) + } + ast::ColumnConstraint::GeneratedConstraint(constraint) => { + build_generated_constraint(constraint) + } + ast::ColumnConstraint::NotNullConstraint(constraint) => { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(not) = constraint.not_token() { + doc = doc + .append(leading_comments_token(¬)) + .append(Doc::text("not")); + } + if let Some(null) = constraint.null_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&null)) + .append(Doc::text("null")); + } + if let Some(column) = constraint.column_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + } + append_constraint_options(doc, constraint.constraint_options()) + .nest(2) + .group() + } + ast::ColumnConstraint::NullConstraint(constraint) => { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(null) = constraint.null_token() { + doc = doc + .append(leading_comments_token(&null)) + .append(Doc::text("null")); + } + append_constraint_options(doc, constraint.constraint_options()) + .nest(2) + .group() + } + ast::ColumnConstraint::PrimaryKeyConstraint(constraint) => { + build_primary_key_constraint(constraint) + } + ast::ColumnConstraint::ReferencesConstraint(constraint) => { + build_references_constraint(constraint) + } + ast::ColumnConstraint::UniqueConstraint(constraint) => build_unique_constraint(constraint), + } +} + +fn build_references_constraint<'a>(constraint: ast::ReferencesConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(references) = constraint.references_token() { + doc = doc + .append(leading_comments_token(&references)) + .append(Doc::text("references")); + } + if let Some(table) = constraint.table() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table.syntax())); + if let Some(path) = table.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(column) = constraint.column() { + if let Some(l_paren) = constraint.l_paren_token() { + doc = doc.append(comments_before(l_paren)); + } + doc = doc + .append(Doc::text("(")) + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + if let Some(r_paren) = constraint.r_paren_token() { + doc = doc.append(comments_before(r_paren)); + } + doc = doc.append(Doc::text(")")); + } + if let Some(match_type) = constraint.match_type() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(match_type.syntax())) + .append(build_keyword_node(match_type.syntax())); + } + if let Some(action) = constraint.on_delete_action() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(action.syntax())) + .append(build_reference_action( + action.on_token(), + action.delete_token(), + "delete", + action.ref_action(), + )); + } + if let Some(action) = constraint.on_update_action() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(action.syntax())) + .append(build_reference_action( + action.on_token(), + action.update_token(), + "update", + action.ref_action(), + )); + } + append_constraint_options(doc, constraint.constraint_options()) + .nest(2) + .group() +} + +fn build_generated_constraint<'a>(constraint: ast::GeneratedConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(generated) = constraint.generated_token() { + doc = doc + .append(leading_comments_token(&generated)) + .append(Doc::text("generated")); + } + if let Some(generated_as) = constraint.generated_as() { + doc = doc + .append(Doc::space()) + .append(leading_comments(generated_as.syntax())) + .append(match generated_as { + ast::GeneratedAs::GeneratedIdentity(identity) => { + let mut body = identity + .generated_when() + .map(build_generated_when) + .unwrap_or_else(Doc::nil); + if let Some(as_token) = identity.as_token() { + body = body + .append(Doc::space()) + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")); + } + if let Some(identity_token) = identity.identity_token() { + body = body + .append(Doc::space()) + .append(leading_comments_token(&identity_token)) + .append(Doc::text("identity")); + } + if let Some(options) = identity.sequence_option_list() { + body = body + .append(Doc::space()) + .append(leading_comments(options.syntax())) + .append(build_sequence_option_list(options)); + } + body + } + ast::GeneratedAs::GeneratedStored(stored) => { + let mut body = stored + .generated_when() + .map(build_generated_when) + .unwrap_or_else(Doc::nil); + if let Some(as_token) = stored.as_token() { + body = body + .append(Doc::space()) + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")); + } + if let Some(l_paren) = stored.l_paren_token() { + if comment_tokens_before(l_paren.clone()).is_empty() { + body = body.append(Doc::space()); + } else { + body = body.append(comments_before(l_paren)); + } + } + let mut expr = stored + .expr() + .map(|expr| leading_comments(expr.syntax()).append(build_expr(expr))) + .unwrap_or_else(Doc::nil); + if let Some(r_paren) = stored.r_paren_token() { + expr = expr.append(comments_before(r_paren)); + } + body = body + .append(Doc::text("(")) + .append(wrap_body(expr)) + .append(Doc::text(")")); + if let Some(kind) = stored.generated_kind() { + body = body + .append(Doc::space()) + .append(leading_comments(kind.syntax())) + .append(build_keyword_node(kind.syntax())); + } + body.group() + } + }); + } + doc = doc.group(); + for option in constraint.constraint_options() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(option.syntax())) + .append(build_keyword_node(option.syntax())) + .nest(2), + ); + } + doc.group() +} + +fn build_generated_when<'a>(when: ast::GeneratedWhen) -> Doc<'a> { + match when { + ast::GeneratedWhen::GeneratedAlways(always) => build_keyword_node(always.syntax()), + ast::GeneratedWhen::GeneratedByDefault(by_default) => { + build_keyword_node(by_default.syntax()) + } + } +} + +fn build_sequence_option_list<'a>(list: ast::SequenceOptionList) -> Doc<'a> { + let doc = list + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let options = list + .sequence_options() + .map(|option| leading_comments(option.syntax()).append(build_sequence_option(option))); + let mut body = Doc::list(Itertools::intersperse(options, Doc::line_or_space()).collect()); + if let Some(r_paren) = list.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")) +} + +fn build_sequence_option<'a>(option: ast::SequenceOption) -> Doc<'a> { + match option { + ast::SequenceOption::OptionAsType(option) => { + let mut doc = Doc::text("as"); + if let Some(ty) = option.ty() { + doc = doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + doc + } + ast::SequenceOption::OptionCache(option) => { + append_optional_literal(Doc::text("cache"), option.literal()) + } + ast::SequenceOption::OptionIncrement(option) => { + let mut doc = option + .increment_token() + .map(|token| leading_comments_token(&token).append(Doc::text("increment"))) + .unwrap_or_else(Doc::nil); + if let Some(by) = option.by_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&by)) + .append(Doc::text("by")); + } + append_optional_literal(doc, option.literal()) + } + ast::SequenceOption::OptionMaxValue(option) => { + append_optional_literal(Doc::text("maxvalue"), option.literal()) + } + ast::SequenceOption::OptionMinValue(option) => { + append_optional_literal(Doc::text("minvalue"), option.literal()) + } + ast::SequenceOption::OptionRestart(option) => { + let mut doc = option + .restart_token() + .map(|token| leading_comments_token(&token).append(Doc::text("restart"))) + .unwrap_or_else(Doc::nil); + if let Some(with) = option.with_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + append_optional_literal(doc, option.literal()) + } + ast::SequenceOption::OptionStart(option) => { + let mut doc = option + .start_token() + .map(|token| leading_comments_token(&token).append(Doc::text("start"))) + .unwrap_or_else(Doc::nil); + if let Some(with) = option.with_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + append_optional_literal(doc, option.literal()) + } + ast::SequenceOption::OptionOwnedBy(option) => { + let mut doc = option + .owned_token() + .map(|token| leading_comments_token(&token).append(Doc::text("owned"))) + .unwrap_or_else(Doc::nil); + if let Some(by) = option.by_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&by)) + .append(Doc::text("by")); + } + if let Some(target) = option.owned_by_target() { + doc = doc + .append(Doc::space()) + .append(leading_comments(target.syntax())) + .append(match target { + ast::OwnedByTarget::OwnedByNone(_) => Doc::text("none"), + ast::OwnedByTarget::QualifiedColumnNameRef(name) => name + .path_ref() + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil), + }); + } + doc + } + ast::SequenceOption::OptionSequenceName(option) => { + let mut doc = option + .sequence_token() + .map(|token| leading_comments_token(&token).append(Doc::text("sequence"))) + .unwrap_or_else(Doc::nil); + if let Some(name) = option.name_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&name)) + .append(Doc::text("name")); + } + if let Some(sequence) = option.sequence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sequence.syntax())); + if let Some(path) = sequence.path() { + doc = doc.append(build_path(&path)); + } + } + doc + } + ast::SequenceOption::OptionCycle(option) => build_keyword_node(option.syntax()), + ast::SequenceOption::OptionLogged(option) => build_keyword_node(option.syntax()), + ast::SequenceOption::OptionNoCycle(option) => build_keyword_node(option.syntax()), + ast::SequenceOption::OptionNoMaxValue(option) => build_keyword_node(option.syntax()), + ast::SequenceOption::OptionNoMinValue(option) => build_keyword_node(option.syntax()), + ast::SequenceOption::OptionUnlogged(option) => build_keyword_node(option.syntax()), + } +} + +fn append_optional_literal<'a>(doc: Doc<'a>, literal: Option) -> Doc<'a> { + literal.map_or(doc.clone(), |literal| { + doc.append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)) + }) +} + +fn build_table_constraint<'a>(constraint: ast::TableConstraint) -> Doc<'a> { + match constraint { + ast::TableConstraint::CheckConstraint(constraint) => build_check_constraint(constraint), + ast::TableConstraint::ExcludeConstraint(constraint) => build_exclude_constraint(constraint), + ast::TableConstraint::ForeignKeyConstraint(constraint) => { + build_foreign_key_constraint(constraint) + } + ast::TableConstraint::PrimaryKeyConstraint(constraint) => { + build_primary_key_constraint(constraint) + } + ast::TableConstraint::UniqueConstraint(constraint) => build_unique_constraint(constraint), + } +} + +fn build_constraint_name_clause<'a>(clause: Option) -> Doc<'a> { + let Some(clause) = clause else { + return Doc::nil(); + }; + let mut doc = Doc::text("constraint"); + if let Some(name) = clause.constraint_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc.append(Doc::space()) +} + +fn build_check_constraint<'a>(constraint: ast::CheckConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(check) = constraint.check_token() { + doc = doc + .append(leading_comments_token(&check)) + .append(Doc::text("check")); + } + if let Some(l_paren) = constraint.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&l_paren)); + } + doc = doc.append(Doc::text("(")); + + let mut body = Doc::nil(); + if let Some(expr) = constraint.expr() { + body = body + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + if let Some(r_paren) = constraint.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); + + let mut options = Doc::nil(); + for option in constraint.constraint_options() { + options = options + .append(Doc::line_or_space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_node(option.syntax())); + } + doc.append(options.nest(2)).group() +} + +fn build_primary_key_constraint<'a>(constraint: ast::PrimaryKeyConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(primary) = constraint.primary_token() { + doc = doc + .append(leading_comments_token(&primary)) + .append(Doc::text("primary")); + } + if let Some(key) = constraint.key_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&key)) + .append(Doc::text("key")); + } + if let Some(using_index) = constraint.using_index() { + doc = doc + .append(Doc::space()) + .append(leading_comments(using_index.syntax())) + .append(build_using_index_name(using_index)); + } else if let Some(parameters) = constraint.index_parameters() { + if has_index_parameters(¶meters) { + doc = doc + .append(Doc::space()) + .append(leading_comments(parameters.syntax())) + .append(build_index_parameters(parameters)); + } + } + let options = append_constraint_options(Doc::nil(), constraint.constraint_options()).nest(2); + doc.append(options).group() +} + +fn build_unique_constraint<'a>(constraint: ast::UniqueConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(unique) = constraint.unique_token() { + doc = doc + .append(leading_comments_token(&unique)) + .append(Doc::text("unique")); + } + if let Some(using_index) = constraint.using_index() { + doc = doc + .append(Doc::space()) + .append(leading_comments(using_index.syntax())) + .append(build_using_index_name(using_index)); + } else if let Some(parameters) = constraint.index_parameters() { + if has_index_parameters(¶meters) { + doc = doc + .append(Doc::space()) + .append(leading_comments(parameters.syntax())) + .append(build_index_parameters(parameters)); + } + } + let options = append_constraint_options(Doc::nil(), constraint.constraint_options()).nest(2); + doc.append(options).group() +} + +fn build_using_index_name<'a>(using_index: ast::UsingIndexName) -> Doc<'a> { + let mut doc = Doc::text("using"); + if let Some(index) = using_index.index_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&index)) + .append(Doc::text("index")); + } + if let Some(index) = using_index.index_ref() { + if let Some(path) = index.path_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(index.syntax())) + .append(build_path_ref(&path)); + } + } + doc +} + +fn has_index_parameters(parameters: &ast::IndexParameters) -> bool { + parameters.nulls_distinct_option().is_some() + || parameters.column_list().is_some() + || parameters.constraint_include_clause().is_some() + || parameters.with_params().is_some() + || parameters.constraint_index_tablespace().is_some() +} + +fn build_index_parameters<'a>(parameters: ast::IndexParameters) -> Doc<'a> { + let mut prefix = parameters + .nulls_distinct_option() + .map(|nulls| leading_comments(nulls.syntax()).append(build_keyword_node(nulls.syntax()))); + if let Some(columns) = parameters.column_list() { + let columns = + leading_comments(columns.syntax()).append(build_constraint_column_ref_list(columns)); + prefix = Some(match prefix { + Some(prefix) => prefix.append(Doc::space()).append(columns), + None => columns, + }); + } + let mut doc = prefix.unwrap_or_else(Doc::nil); + if let Some(include) = parameters.constraint_include_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(include.syntax())) + .append(build_constraint_include_clause(include)) + .nest(2), + ); + } + if let Some(with_params) = parameters.with_params() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(with_params.syntax())) + .append(build_with_params(with_params)) + .nest(2), + ); + } + if let Some(tablespace) = parameters.constraint_index_tablespace() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(tablespace.syntax())) + .append(build_constraint_index_tablespace(tablespace)) + .nest(2), + ); + } + doc +} + +fn build_constraint_column_ref_list<'a>(list: ast::ConstraintColumnRefList) -> Doc<'a> { + let suffix = list.without_overlaps().map(|overlaps| { + Doc::space() + .append(leading_comments(overlaps.syntax())) + .append(build_keyword_node(overlaps.syntax())) + }); + build_column_names( + list.l_paren_token(), + list.column_name_refs(), + suffix, + list.r_paren_token(), + ) +} + +fn build_column_ref_list<'a>(list: ast::ColumnRefList) -> Doc<'a> { + build_column_names( + list.l_paren_token(), + list.column_name_refs(), + None, + list.r_paren_token(), + ) +} + +fn build_column_names<'a>( + l_paren: Option, + names: impl Iterator, + suffix: Option>, + r_paren: Option, +) -> Doc<'a> { + let doc = l_paren + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let items = names.map(|name| { + ( + leading_comments(name.syntax()).append(build_name(name.syntax())), + name.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(suffix) = suffix { + body = body.append(suffix); + } + if let Some(r_paren) = r_paren { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_constraint_include_clause<'a>(include: ast::ConstraintIncludeClause) -> Doc<'a> { + let mut doc = Doc::text("include"); + if let Some(columns) = include.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); + } + doc +} + +fn build_with_params<'a>(with_params: ast::WithParams) -> Doc<'a> { + let mut doc = Doc::text("with"); + if let Some(attributes) = with_params.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + doc +} + +fn build_attribute_list<'a>(list: &ast::AttributeList) -> Doc<'a> { + build_attribute_list_with_layout(list, false) +} + +fn build_multiline_attribute_list<'a>(list: &ast::AttributeList) -> Doc<'a> { + build_attribute_list_with_layout(list, true) +} + +fn build_attribute_list_with_layout<'a>(list: &ast::AttributeList, multiline: bool) -> Doc<'a> { + let doc = list + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let items = list.attribute_options().map(|option| { + let mut item = option + .namespace() + .map(|namespace| build_name(namespace.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(dot) = option.dot_token() { + item = item.append(comments_before(dot)).append(Doc::text(".")); + } + if let Some(name) = option.name() { + item = item + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(eq) = option.eq_token() { + item = item + .append(Doc::space()) + .append(leading_comments_token(&eq)) + .append(Doc::text("=")); + } + if let Some(value) = option.attribute_value() { + item = item + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_attribute_value(value)); + } + ( + leading_comments(option.syntax()).append(item), + option.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = list.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + let body = if multiline { + Doc::hard_line() + .append(body) + .nest(2) + .append(Doc::hard_line()) + } else { + wrap_body(body) + }; + doc.append(body).append(Doc::text(")")).group() +} + +fn build_attribute_value<'a>(value: ast::AttributeValue) -> Doc<'a> { + if let Some(literal) = value.literal() { + build_literal(literal) + } else if let Some(ty) = value.ty() { + build_type(ty) + } else if let Some(custom_op) = value.custom_op() { + build_custom_operator(custom_op) + } else if value.none_token().is_some() { + Doc::text("none") + } else if let Some(op) = value.op() { + if value.operator_token().is_some() { + let mut doc = Doc::text("operator"); + if let Some(l_paren) = value.l_paren_token() { + doc = doc.append(comments_before(l_paren)); + } + doc = doc.append(Doc::text("(")).append(build_operator(&op)); + if let Some(r_paren) = value.r_paren_token() { + doc = doc.append(comments_before(r_paren)); + } + doc.append(Doc::text(")")) + } else { + build_operator(&op) + } + } else { + Doc::nil() + } +} + +fn build_constraint_index_tablespace<'a>(tablespace: ast::ConstraintIndexTablespace) -> Doc<'a> { + let mut doc = Doc::text("using"); + if let Some(index) = tablespace.index_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&index)) + .append(Doc::text("index")); + } + if let Some(token) = tablespace.tablespace_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("tablespace")); + } + if let Some(name) = tablespace.tablespace_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc +} + +fn append_constraint_options<'a>( + mut doc: Doc<'a>, + options: impl Iterator, +) -> Doc<'a> { + for option in options { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_node(option.syntax())); + } + doc +} + +fn build_foreign_key_constraint<'a>(constraint: ast::ForeignKeyConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(foreign) = constraint.foreign_token() { + doc = doc + .append(leading_comments_token(&foreign)) + .append(Doc::text("foreign")); + } + if let Some(key) = constraint.key_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&key)) + .append(Doc::text("key")); + } + if let Some(columns) = constraint.from_columns() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_foreign_key_column_list(columns)); + } + + let mut references_clause = constraint + .references_token() + .map(|references| leading_comments_token(&references).append(Doc::text("references"))); + if let Some(table) = constraint.table_name_ref() { + if let Some(path) = table.path_ref() { + references_clause = Some( + references_clause + .unwrap_or_else(Doc::nil) + .append(Doc::space()) + .append(leading_comments(table.syntax())) + .append(build_path_ref(&path)), + ); + } + } + if let Some(columns) = constraint.to_columns() { + references_clause = Some( + references_clause + .unwrap_or_else(Doc::nil) + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_foreign_key_column_list(columns)), + ); + } + if let Some(references_clause) = references_clause { + doc = doc.append(Doc::line_or_space().append(references_clause).nest(2)); + } + if let Some(match_type) = constraint.match_type() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(match_type.syntax())) + .append(build_keyword_node(match_type.syntax())) + .nest(2), + ); + } + if let Some(action) = constraint.on_delete_action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_reference_action( + action.on_token(), + action.delete_token(), + "delete", + action.ref_action(), + )) + .nest(2), + ); + } + if let Some(action) = constraint.on_update_action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_reference_action( + action.on_token(), + action.update_token(), + "update", + action.ref_action(), + )) + .nest(2), + ); + } + let options = append_constraint_options(Doc::nil(), constraint.constraint_options()).nest(2); + doc.append(options).group() +} + +fn build_foreign_key_column_list<'a>(list: ast::ForeignKeyColumnList) -> Doc<'a> { + let suffix = list.period_column().map(|period| { + let mut doc = Doc::space() + .append(leading_comments(period.syntax())) + .append(Doc::text("period")); + if let Some(name) = period.name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc + }); + build_column_names( + list.l_paren_token(), + list.column_name_refs(), + suffix, + list.r_paren_token(), + ) +} + +fn build_reference_action<'a>( + on: Option, + kind_token: Option, + kind: &'static str, + action: Option, +) -> Doc<'a> { + let mut doc = on + .map(|token| leading_comments_token(&token).append(Doc::text("on"))) + .unwrap_or_else(Doc::nil); + if let Some(token) = kind_token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(kind)); + } + let Some(action) = action else { + return doc; + }; + doc = doc + .append(Doc::space()) + .append(leading_comments(action.syntax())); + match action { + ast::RefAction::SetNullColumns(action) => { + if let Some(set) = action.set_token() { + doc = doc + .append(leading_comments_token(&set)) + .append(Doc::text("set")); + } + if let Some(null) = action.null_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&null)) + .append(Doc::text("null")); + } + if let Some(columns) = action.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); + } + doc + } + ast::RefAction::SetDefaultColumns(action) => { + if let Some(set) = action.set_token() { + doc = doc + .append(leading_comments_token(&set)) + .append(Doc::text("set")); + } + if let Some(default) = action.default_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } + if let Some(columns) = action.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); + } + doc + } + action => doc.append(build_keyword_node(action.syntax())), + } +} + +fn build_exclude_constraint<'a>(constraint: ast::ExcludeConstraint) -> Doc<'a> { + let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); + if let Some(exclude) = constraint.exclude_token() { + doc = doc + .append(leading_comments_token(&exclude)) + .append(Doc::text("exclude")); + } + if let Some(method) = constraint.constraint_index_method() { + doc = doc + .append(Doc::space()) + .append(leading_comments(method.syntax())) + .append(Doc::text("using")); + if let Some(name) = method.access_method_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + } + if let Some(list) = constraint.constraint_exclusion_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_constraint_exclusion_list(list)); + } + if let Some(include) = constraint.constraint_include_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(include.syntax())) + .append(build_constraint_include_clause(include)) + .nest(2), + ); + } + if let Some(with_params) = constraint.with_params() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(with_params.syntax())) + .append(build_with_params(with_params)) + .nest(2), + ); + } + if let Some(tablespace) = constraint.constraint_index_tablespace() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(tablespace.syntax())) + .append(build_constraint_index_tablespace(tablespace)) + .nest(2), + ); + } + if let Some(where_clause) = constraint.where_condition_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(where_clause.syntax())) + .append(build_where_condition_clause(where_clause)) + .nest(2), + ); + } + let options = append_constraint_options(Doc::nil(), constraint.constraint_options()).nest(2); + doc.append(options).group() +} + +fn build_constraint_exclusion_list<'a>(list: ast::ConstraintExclusionList) -> Doc<'a> { + let doc = list + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let items = list.constraint_exclusions().map(|exclusion| { + let mut item = exclusion.expr().map(build_expr).unwrap_or_else(Doc::nil); + if let Some(with) = exclusion.with_token() { + item = item + .append(Doc::line_or_space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + if let Some(op) = exclusion.op() { + item = item + .append(Doc::space()) + .append(leading_comments(op.syntax())) + .append(build_operator(&op)); + } else if let Some(op) = exclusion.operator_call() { + item = item + .append(Doc::space()) + .append(leading_comments(op.syntax())) + .append(build_operator_call(&op)); + } + ( + leading_comments(exclusion.syntax()).append(item.nest(2).group()), + exclusion.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = list.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_where_condition_clause<'a>(where_clause: ast::WhereConditionClause) -> Doc<'a> { + let mut doc = Doc::text("where"); + if let Some(l_paren) = where_clause.l_paren_token() { + doc = doc.append(space_before_l_paren(l_paren)); + } + doc = doc.append(Doc::text("(")); + + let mut body = Doc::nil(); + if let Some(expr) = where_clause.expr() { + body = body + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + if let Some(r_paren) = where_clause.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_like_clause<'a>(like_clause: &ast::LikeClause) -> Doc<'a> { + let mut doc = Doc::text("like"); + + if let Some(relation_name) = like_clause.relation_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(relation_name.syntax())); + if let Some(path) = relation_name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + + let options: Vec> = like_clause + .like_options() + .map(|option| { + Doc::line_or_space() + .append(leading_comments(option.syntax())) + .append(build_like_option(&option)) + }) + .collect(); + if !options.is_empty() { + doc = doc.append(Doc::list(options).nest(2).group()); + } + + doc +} + +fn build_like_option<'a>(option: &ast::LikeOption) -> Doc<'a> { + let (keyword, property) = match option { + ast::LikeOption::ExcludingProperty(n) => ("excluding", n.table_property()), + ast::LikeOption::IncludingProperty(n) => ("including", n.table_property()), + }; + + let mut doc = Doc::text(keyword); + if let Some(property) = property { + doc = doc + .append(Doc::space()) + .append(leading_comments(property.syntax())) + .append(build_keyword_node(property.syntax())); + } + doc +} + +fn build_values<'a>(values: &ast::Values) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(with_clause) = values.with_clause() { + doc = doc + .append(leading_comments(with_clause.syntax())) + .append(build_with_clause(with_clause)) + .append(Doc::hard_line()); + if let Some(values_token) = values.values_token() { + doc = doc.append(leading_comments_token(&values_token)); + } + } + + let mut values_doc = Doc::text("values"); + if let Some(row_list) = values.row_list() { + let rows = row_list.rows().map(|row| { + ( + leading_comments(row.syntax()).append(build_row(row.clone())), + row.syntax().clone(), + ) + }); + if let Some(rows) = build_comma_separated_docs(rows) { + values_doc = values_doc.append( + Doc::space() + .append(leading_comments(row_list.syntax())) + .append(rows), + ); + } + } + doc = doc.append(values_doc.group()); + + if let Some(order_by) = values.order_by_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(order_by.syntax())) + .append(build_order_by_clause(order_by)); + } + for locking in values.locking_clauses() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(locking.syntax())) + .append(build_locking_clause(locking)); + } + if let Some(limit) = values.limit_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(limit.syntax())) + .append(build_limit_clause(limit)); + } + if let Some(fetch) = values.fetch_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(fetch.syntax())) + .append(build_fetch_clause(fetch)); + } + if let Some(offset) = values.offset_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(offset.syntax())) + .append(build_offset_clause(offset)); + } + + doc.append(build_semicolon(values.semicolon_token())) + .group() +} + +fn build_row<'a>(row: ast::Row) -> Doc<'a> { + let mut doc = row + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let exprs = build_comma_separated_exprs(row.exprs()); + let has_exprs = exprs.is_some(); + let mut body = exprs.unwrap_or_else(Doc::nil); + if !has_exprs { + if let Some(r_paren) = row.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + } + doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); + doc +} + +fn build_table<'a>(table: &ast::Table) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(with_clause) = table.with_clause() { + doc = doc + .append(leading_comments(with_clause.syntax())) + .append(build_with_clause(with_clause)) + .append(Doc::hard_line()); + if let Some(table_token) = table.table_token() { + doc = doc.append(leading_comments_token(&table_token)); + } + } + + let mut table_doc = Doc::text("table"); + if let Some(relation) = table.relation_name() { + table_doc = table_doc.append( + Doc::line_or_space() + .append(leading_comments(relation.syntax())) + .append(build_relation_name(relation)) + .nest(2), + ); + } + doc = doc.append(table_doc.group()); + + if let Some(order_by) = table.order_by_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(order_by.syntax())) + .append(build_order_by_clause(order_by)); + } + for locking in table.locking_clauses() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(locking.syntax())) + .append(build_locking_clause(locking)); + } + if let Some(limit) = table.limit_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(limit.syntax())) + .append(build_limit_clause(limit)); + } + if let Some(fetch) = table.fetch_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(fetch.syntax())) + .append(build_fetch_clause(fetch)); + } + if let Some(offset) = table.offset_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(offset.syntax())) + .append(build_offset_clause(offset)); + } + + doc.append(build_semicolon(table.semicolon_token())).group() +} + +fn build_relation_name<'a>(relation: ast::RelationName) -> Doc<'a> { + let mut doc = Doc::nil(); + let has_only = relation.only_token().is_some(); + if let Some(only) = relation.only_token() { + doc = doc + .append(leading_comments_token(&only)) + .append(Doc::text("only")); + } + if let Some(l_paren) = relation.l_paren_token() { + if has_only && comment_tokens_before(l_paren.clone()).is_empty() { + doc = doc.append(Doc::space()); + } + doc = doc.append(comments_before(l_paren)); + doc = doc.append(Doc::text("(")); + } + if let Some(name) = relation.relation_name_ref() { + if has_only && relation.l_paren_token().is_none() { + doc = doc.append(Doc::space()); + } + doc = doc.append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(r_paren) = relation.r_paren_token() { + doc = doc.append(comments_before(r_paren)); + doc = doc.append(Doc::text(")")); + } + if let Some(star) = relation.star_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&star)) + .append(Doc::text("*")); + } + doc +} + +fn build_select_into<'a>(select_into: &ast::SelectInto) -> Doc<'a> { + let mut select_body = Doc::nil(); + if let Some(select_clause) = select_into.select_clause() { + match select_clause.select_quantifier() { + Some(ast::SelectQuantifier::DistinctClause(distinct_clause)) => { + select_body = select_body + .append(leading_comments(distinct_clause.syntax())) + .append(Doc::text("distinct")); + if let Some(distinct_on) = distinct_clause.distinct_on() { + select_body = select_body + .append(Doc::space()) + .append(leading_comments(distinct_on.syntax())) + .append(build_distinct_on(distinct_on)); + } + select_body = select_body.append(Doc::space()); + } + Some(ast::SelectQuantifier::All(all)) => { + select_body = select_body + .append(leading_comments(all.syntax())) + .append(Doc::text("all")) + .append(Doc::space()); + } + None => (), + } + if let Some(target_list) = select_clause.target_list() { + select_body = select_body + .append(leading_comments(target_list.syntax())) + .append(Doc::list( + Itertools::intersperse( + target_list.targets().flat_map(build_target), + Doc::text(",").append(Doc::line_or_space()), + ) + .collect(), + )); + } + } + let mut doc = Doc::nil(); + if let Some(with_clause) = select_into.with_clause() { + doc = doc + .append(leading_comments(with_clause.syntax())) + .append(build_with_clause(with_clause)) + .append(Doc::hard_line()); + } + if select_into.with_clause().is_some() { + if let Some(select_clause) = select_into.select_clause() { + doc = doc.append(leading_comments(select_clause.syntax())); + } + } + doc = doc.append( + Doc::text("select") + .append(Doc::line_or_space().append(select_body).nest(2)) + .group(), + ); + + if let Some(into) = select_into.into_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(into.syntax())) + .append(build_into_clause(into)); + } + if let Some(from) = select_into.from_clause() { + doc = doc + .group() + .append(Doc::line_or_space()) + .append(leading_comments(from.syntax())) + .append(build_from_clause(from)); + } + if let Some(where_clause) = select_into.where_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(where_clause.syntax())) + .append(build_where_clause(where_clause)); + } + if let Some(group) = select_into.group_by_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(group.syntax())) + .append(build_select_group_by_clause(group)); + } + if let Some(having) = select_into.having_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(having.syntax())) + .append(build_having_clause(having)); + } + if let Some(window) = select_into.window_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(window.syntax())) + .append(build_window_clause(window)); + } + if let Some(order_by) = select_into.order_by_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(order_by.syntax())) + .append(build_order_by_clause(order_by)); + } + for locking in select_into.locking_clauses() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(locking.syntax())) + .append(build_locking_clause(locking)); + } + if let Some(limit) = select_into.limit_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(limit.syntax())) + .append(build_limit_clause(limit)); + } + if let Some(offset) = select_into.offset_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(offset.syntax())) + .append(build_offset_clause(offset)); + } + if let Some(filter) = select_into.filter_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(filter.syntax())) + .append(build_filter_clause(filter)); + } + doc.append(build_semicolon(select_into.semicolon_token())) + .group() +} + +fn build_with_clause<'a>(with_clause: ast::WithClause) -> Doc<'a> { + let mut doc = Doc::text("with"); + if let Some(recursive) = with_clause.recursive_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&recursive)) + .append(Doc::text("recursive")); + } + let tables = with_clause.with_tables().map(|table| { + ( + leading_comments(table.syntax()).append(build_with_table(table.clone())), + table.syntax().clone(), + ) + }); + if let Some(tables) = build_comma_separated_docs(tables) { + doc = doc.append(Doc::space()).append(tables); + } + doc +} + +fn build_with_table<'a>(table: ast::WithTable) -> Doc<'a> { + let mut doc = table + .name() + .map(|name| build_name(name.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(columns) = table.column_list() { + doc = doc + .append(leading_comments(columns.syntax())) + .append(build_cte_column_list(columns)); + } + if let Some(as_token) = table.as_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")); + } + if let Some(materialized) = table.materialized_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(materialized.syntax())) + .append(match materialized { + ast::MaterializedOption::Materialized(_) => Doc::text("materialized"), + ast::MaterializedOption::NotMaterialized(not_materialized) => Doc::text("not") + .append(Doc::space()) + .append( + not_materialized + .materialized_token() + .map(|token| leading_comments_token(&token)) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("materialized")), + }); + } + if let Some(l_paren) = table.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&l_paren)) + .append(Doc::text("(")); + } + let mut body = table + .query() + .map(|query| leading_comments(query.syntax()).append(build_with_query(query))) + .unwrap_or_else(Doc::nil); + if let Some(r_paren) = table.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc = doc + .append(Doc::hard_line().append(body).nest(2)) + .append(Doc::hard_line()) + .append(Doc::text(")")); + if let Some(search) = table.search_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(search.syntax())) + .append(build_search_clause(search)); + } + if let Some(cycle) = table.cycle_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(cycle.syntax())) + .append(build_cycle_clause(cycle)); + } + doc.group() +} + +fn build_search_clause<'a>(search: ast::SearchClause) -> Doc<'a> { + let mut doc = Doc::text("search"); + if let Some(order) = search.search_order() { + doc = doc + .append(Doc::space()) + .append(leading_comments(order.syntax())) + .append(match order { + ast::SearchOrder::BreadthFirst(first) => Doc::text("breadth") + .append(Doc::space()) + .append( + first + .first_token() + .map(|token| leading_comments_token(&token)) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("first")), + ast::SearchOrder::DepthFirst(first) => Doc::text("depth") + .append(Doc::space()) + .append( + first + .first_token() + .map(|token| leading_comments_token(&token)) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("first")), + }); + } + if let Some(by_token) = search.by_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&by_token)) + .append(Doc::text("by")); + } + if let Some(columns) = search.columns() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_name_refs(columns.column_name_refs())); + } + if let Some(set_column) = search.set_column() { + doc = doc + .append(Doc::space()) + .append(leading_comments(set_column.syntax())) + .append(build_search_set_column(set_column)); + } + doc.group() +} + +fn build_search_set_column<'a>(set_column: ast::SearchSetColumn) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(column) = set_column.column_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + } + doc +} + +fn build_cycle_clause<'a>(cycle: ast::CycleClause) -> Doc<'a> { + let mut doc = Doc::text("cycle"); + if let Some(columns) = cycle.columns() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_name_refs(columns.column_name_refs())); + } + if let Some(set_column) = cycle.set_column() { + doc = doc + .append(Doc::space()) + .append(leading_comments(set_column.syntax())) + .append(build_cycle_set_column(set_column)); + } + if let Some(path) = cycle.path() { + doc = doc + .append(Doc::space()) + .append(leading_comments(path.syntax())) + .append(build_cycle_path(path)); + } + doc.group() +} + +fn build_cycle_set_column<'a>(set_column: ast::CycleSetColumn) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(column) = set_column.column_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + } + if let Some(column_to) = set_column.column_to() { + doc = doc + .append(Doc::space()) + .append(leading_comments(column_to.syntax())) + .append(build_cycle_column_to(column_to)); + } + doc +} + +fn build_cycle_column_to<'a>(column_to: ast::CycleColumnTo) -> Doc<'a> { + let mut doc = Doc::text("to"); + if let Some(expr) = column_to.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + if let Some(default) = column_to.default() { + doc = doc + .append(Doc::space()) + .append(leading_comments(default.syntax())) + .append(Doc::text("default")); + if let Some(expr) = default.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + } + doc +} + +fn build_cycle_path<'a>(path: ast::CyclePath) -> Doc<'a> { + let mut doc = Doc::text("using"); + if let Some(column) = path.column_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + } + doc +} + +fn build_column_name_refs<'a>(columns: impl Iterator) -> Doc<'a> { + let columns = columns.map(|column| { + ( + leading_comments(column.syntax()).append(build_name(column.syntax())), + column.syntax().clone(), + ) + }); + build_comma_separated_docs(columns).unwrap_or_else(Doc::nil) +} + +fn build_cte_column_list<'a>(columns: ast::ColumnList) -> Doc<'a> { + let mut doc = columns + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")); + let names = columns.column_names().map(|name| { + ( + leading_comments(name.syntax()).append(build_name(name.syntax())), + name.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(names).unwrap_or_else(Doc::nil); + if let Some(r_paren) = columns.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); + doc +} + +fn build_with_query<'a>(query: ast::WithQuery) -> Doc<'a> { + match query { + ast::WithQuery::CompoundSelect(select) => build_compound_select(&select), + ast::WithQuery::ParenSelect(select) => build_paren_select(select), + ast::WithQuery::Select(select) => build_select_doc(&select), + ast::WithQuery::Table(table) => build_table(&table), + ast::WithQuery::Values(values) => build_values(&values), + ast::WithQuery::Delete(delete) => build_delete(&delete), + ast::WithQuery::Insert(insert) => build_insert(&insert), + ast::WithQuery::Merge(merge) => build_merge(&merge), + ast::WithQuery::Update(update) => build_update(&update), + } +} + +fn build_distinct_on<'a>(distinct_on: ast::DistinctOn) -> Doc<'a> { + let mut doc = Doc::text("on"); + if let Some(l_paren) = distinct_on.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&l_paren)) + .append(Doc::text("(")); + } + let exprs: Vec<_> = distinct_on.exprs().collect(); + let has_exprs = !exprs.is_empty(); + let mut body = build_comma_separated_exprs(exprs.into_iter()).unwrap_or_else(Doc::nil); + if !has_exprs { + if let Some(r_paren) = distinct_on.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_having_clause<'a>(having: ast::HavingClause) -> Doc<'a> { + let mut doc = Doc::text("having"); + if let Some(expr) = having.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + doc +} + +fn build_window_clause<'a>(window: ast::WindowClause) -> Doc<'a> { + let defs = window.window_defs().map(|def| { + ( + leading_comments(def.syntax()).append(build_window_def(def.clone())), + def.syntax().clone(), + ) + }); + let mut doc = Doc::text("window"); + if let Some(defs) = build_comma_separated_docs(defs) { + doc = doc.append(Doc::space()).append(defs.nest(2)); + } + doc.group() +} + +fn build_window_def<'a>(def: ast::WindowDef) -> Doc<'a> { + let mut doc = def + .window() + .map(|window| build_name(window.syntax())) + .unwrap_or_else(Doc::nil); + if let Some(as_token) = def.as_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&as_token)) + .append(Doc::text("as")); + } + if let Some(l_paren) = def.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&l_paren)) + .append(Doc::text("(")); + } + let mut body = def + .window_spec() + .map(|spec| leading_comments(spec.syntax()).append(build_window_spec(spec))) + .unwrap_or_else(Doc::nil); + if let Some(r_paren) = def.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_into_clause<'a>(into: ast::IntoClause) -> Doc<'a> { + let mut doc = Doc::text("into"); + if let Some(persistence) = into.persistence() { + doc = doc + .append(Doc::space()) + .append(leading_comments(persistence.syntax())) + .append(build_keyword_node(persistence.syntax())); + } + if let Some(table_token) = into.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&table_token)) + .append(Doc::text("table")); + } + if let Some(table_name) = into.table_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(table_name.syntax())); + if let Some(path) = table_name.path() { + doc = doc.append(build_path(&path)); + } + } + doc +} + +fn build_select_group_by_clause<'a>(group: ast::GroupByClause) -> Doc<'a> { + let mut doc = Doc::text("group").append(Doc::space()); + if let Some(by_token) = group.by_token() { + doc = doc.append(leading_comments_token(&by_token)); + } + doc = doc.append(Doc::text("by")).append(Doc::space()); + if let Some(quantifier) = group.all_or_distinct() { + doc = doc + .append(leading_comments(quantifier.syntax())) + .append(match quantifier { + ast::AllOrDistinct::All(_) => Doc::text("all"), + ast::AllOrDistinct::Distinct(_) => Doc::text("distinct"), + }) + .append(Doc::space()); + } + if let Some(list) = group.group_by_list() { + doc = doc.append(build_group_by_list(list)); + } + doc +} + +fn build_create_publication<'a>(stmt: &ast::CreatePublication) -> Doc<'a> { + let mut doc = Doc::text("create"); + if let Some(token) = stmt.publication_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("publication")); + } + if let Some(publication) = stmt.publication() { + doc = doc + .append(Doc::space()) + .append(leading_comments(publication.syntax())) + .append(build_name(publication.syntax())); + } + if let Some(clause) = stmt.publication_for_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(clause.syntax())) + .append(build_publication_for_clause(clause)) + .nest(2), + ); + } + if let Some(params) = stmt.with_params() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(params.syntax())) + .append(build_with_params(params)) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() +} + +fn build_publication_for_clause<'a>(clause: ast::PublicationForClause) -> Doc<'a> { + match clause { + ast::PublicationForClause::ForAllPublicationObjects(clause) => { + let mut doc = Doc::text("for"); + if let Some(objects) = build_all_publication_objects(clause.all_publication_objects()) { + doc = doc.append(Doc::line_or_space().append(objects).nest(2)); + } + if let Some(except) = clause.except_table_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(except.syntax())) + .append(build_except_table_clause(except)); + } + doc.group() + } + ast::PublicationForClause::ForPublicationObjects(clause) => { + let mut doc = Doc::text("for"); + if let Some(objects) = build_publication_objects(clause.publication_objects()) { + doc = doc.append(Doc::line_or_space().append(objects).nest(2)); + } + doc.group() + } + } +} + +fn build_all_publication_objects<'a>( + objects: impl Iterator, +) -> Option> { + build_comma_separated_docs(objects.map(|object| { + let syntax = object.syntax().clone(); + let doc = leading_comments(object.syntax()).append(match object { + ast::AllPublicationObject::AllPublicationTables(object) => { + let mut doc = Doc::text("all"); + if let Some(token) = object.tables_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("tables")); + } + doc + } + ast::AllPublicationObject::AllPublicationSequences(object) => { + let mut doc = Doc::text("all"); + if let Some(token) = object.sequences_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("sequences")); + } + doc + } + }); + (doc, syntax) + })) +} + +fn build_publication_objects<'a>( + objects: impl Iterator, +) -> Option> { + build_comma_separated_docs(objects.map(|object| { + let syntax = object.syntax().clone(); + ( + leading_comments(object.syntax()).append(build_publication_object(object)), + syntax, + ) + })) +} + +fn build_publication_object<'a>(object: ast::PublicationObject) -> Doc<'a> { + match object { + ast::PublicationObject::PublicationObjectCurrentSchema(_) => Doc::text("current_schema"), + ast::PublicationObject::PublicationObjectTable(object) => { + let mut doc = Doc::text("table"); + if let Some(token) = object.only_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("only")); + } + let parenthesized = object.l_paren_token().is_some(); + if let Some(l_paren) = object.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(comments_before(l_paren)) + .append(Doc::text("(")); + } + if let Some(table) = object.table_name_ref() { + if !parenthesized { + doc = doc.append(Doc::space()); + } + doc = doc.append(leading_comments(table.syntax())); + if let Some(path) = table.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(r_paren) = object.r_paren_token() { + doc = doc.append(comments_before(r_paren)).append(Doc::text(")")); + } + if let Some(star) = object.star_token() { + doc = doc + .append(leading_comments_token(&star)) + .append(Doc::text("*")); + } + if let Some(columns) = object.column_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(columns.syntax())) + .append(build_column_ref_list(columns)); + } + if let Some(where_clause) = object.where_condition_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(where_clause.syntax())) + .append(build_where_condition_clause(where_clause)); + } + doc.group() + } + ast::PublicationObject::PublicationObjectTablesInSchema(object) => { + let mut doc = Doc::text("tables"); + if let Some(token) = object.in_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("in")); + } + if let Some(token) = object.schema_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("schema")); + } + if let Some(token) = object.current_schema_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("current_schema")); + } else if let Some(schema) = object.schema_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(schema.syntax())) + .append(build_name(schema.syntax())); + } + if let Some(where_clause) = object.where_condition_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(where_clause.syntax())) + .append(build_where_condition_clause(where_clause)); + } + doc.group() + } + } +} + +fn build_except_table_clause<'a>(clause: ast::ExceptTableClause) -> Doc<'a> { + let mut doc = Doc::text("except"); + if let Some(l_paren) = clause.l_paren_token() { + doc = doc + .append(Doc::space()) + .append(comments_before(l_paren)) + .append(Doc::text("(")); + } + let items = clause.except_table_names().map(|name| { + let mut item = Doc::nil(); + if let Some(table_token) = name.table_token() { + item = item + .append(leading_comments_token(&table_token)) + .append(Doc::text("table")) + .append(Doc::space()); + } + if let Some(table) = name.table_relation_name() { + item = item + .append(leading_comments(table.syntax())) + .append(build_table_relation_name(table)); + } + ( + leading_comments(name.syntax()).append(item), + name.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = clause.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(wrap_body(body)).append(Doc::text(")")).group() +} + +fn build_alter_foreign_table<'a>(stmt: &ast::AlterForeignTable) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.foreign_token(), "foreign"), + (stmt.table_token(), "table"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(table) = stmt.table_relation_name() { + doc = doc + .append(Doc::space()) + .append(build_table_relation_name(table)); + } + let actions = stmt.actions().map(|action| { + let syntax = action.syntax().clone(); + ( + leading_comments(&syntax).append(build_alter_foreign_table_action(action)), + syntax, + ) + }); + if let Some(actions) = build_comma_separated_docs(actions) { + doc = doc.append(Doc::line_or_space().append(actions).nest(2)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn append_commented_name<'a>(mut doc: Doc<'a>, node: Option) -> Doc<'a> { + if let Some(node) = node { + doc = doc + .append(Doc::space()) + .append(leading_comments(node.syntax())) + .append(build_name(node.syntax())); + } + doc +} + +fn append_commented_constraint_ref<'a>( + mut doc: Doc<'a>, + constraint: Option, +) -> Doc<'a> { + if let Some(constraint) = constraint { + doc = doc + .append(Doc::space()) + .append(leading_comments(constraint.syntax())); + if let Some(path) = constraint.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc +} + +fn append_commented_table_ref<'a>(mut doc: Doc<'a>, table: Option) -> Doc<'a> { + if let Some(table) = table { + doc = doc + .append(Doc::space()) + .append(leading_comments(table.syntax())); + if let Some(path) = table.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc +} + +fn build_alter_foreign_table_action<'a>(action: ast::AlterTableAction) -> Doc<'a> { + match action { + ast::AlterTableAction::AddColumn(node) => { + let mut doc = + build_keyword_tokens([(node.add_token(), "add"), (node.column_token(), "column")]); + if let Some(if_not_exists) = node.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_tokens([ + (if_not_exists.if_token(), "if"), + (if_not_exists.not_token(), "not"), + (if_not_exists.exists_token(), "exists"), + ])); + } + doc = append_commented_name(doc, node.column_name()); + if let Some(ty) = node.ty() { + doc = doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + if let Some(options) = node.alter_option_list() { + let syntax = options.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_alter_option_list(&options)); + } + if let Some(collate) = node.collate() { + let syntax = collate.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_collate_expr(collate)); + } + for constraint in node.constraints() { + let syntax = constraint.syntax().clone(); + doc = append_column_clause(doc, &syntax, build_domain_constraint(constraint)); + } + doc.group() + } + ast::AlterTableAction::AddConstraint(node) => { + let mut doc = Doc::text("add"); + if let Some(constraint) = node.constraint() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(constraint.syntax())) + .append(build_domain_constraint(constraint)) + .nest(2), + ); + } + doc.group() + } + ast::AlterTableAction::AlterColumn(node) => { + let mut doc = build_keyword_tokens([ + (node.alter_token(), "alter"), + (node.column_token(), "column"), + ]); + doc = append_commented_name(doc, node.column_name_ref()); + if let Some(option) = node.option() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(option.syntax())) + .append(build_alter_column_option(option)) + .nest(2), + ); + } + doc.group() + } + ast::AlterTableAction::AlterConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.alter_token(), "alter"), + (node.constraint_token(), "constraint"), + ]); + doc = append_commented_constraint_ref(doc, node.constraint_name_ref()); + if let Some(option) = node.deferrable_constraint_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(Doc::text("deferrable")); + } + if let Some(option) = node.not_deferrable_constraint_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_tokens([ + (option.not_token(), "not"), + (option.deferrable_token(), "deferrable"), + ])); + } + if let Some(option) = node.initially_deferred_constraint_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_tokens([ + (option.initially_token(), "initially"), + (option.deferred_token(), "deferred"), + ])); + } + if let Some(option) = node.initially_immediate_constraint_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(build_keyword_tokens([ + (option.initially_token(), "initially"), + (option.immediate_token(), "immediate"), + ])); + } + doc + } + ast::AlterTableAction::AttachPartition(node) => { + let mut doc = build_keyword_tokens([ + (node.attach_token(), "attach"), + (node.partition_token(), "partition"), + ]); + doc = append_commented_table_ref(doc, node.table_name_ref()); + if let Some(kind) = node.partition_type() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(kind.syntax())) + .append(build_create_table_partition_type(kind)) + .nest(2), + ); + } + doc.group() + } + ast::AlterTableAction::ClusterOn(node) => { + let mut doc = + build_keyword_tokens([(node.cluster_token(), "cluster"), (node.on_token(), "on")]); + if let Some(index) = node.index_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(index.syntax())); + if let Some(path) = index.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc + } + ast::AlterTableAction::DetachPartition(node) => { + let mut doc = build_keyword_tokens([ + (node.detach_token(), "detach"), + (node.partition_token(), "partition"), + ]); + doc = append_commented_table_ref(doc, node.table_name_ref()); + if let Some(option) = node.detach_partition_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(match option { + ast::DetachPartitionOption::DetachConcurrently(n) => { + build_keyword_tokens([(n.concurrently_token(), "concurrently")]) + } + ast::DetachPartitionOption::DetachFinalize(n) => { + build_keyword_tokens([(n.finalize_token(), "finalize")]) + } + }); + } + doc + } + ast::AlterTableAction::DisableRls(node) => build_keyword_tokens([ + (node.disable_token(), "disable"), + (node.row_token(), "row"), + (node.level_token(), "level"), + (node.security_token(), "security"), + ]), + ast::AlterTableAction::EnableRls(node) => build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.row_token(), "row"), + (node.level_token(), "level"), + (node.security_token(), "security"), + ]), + ast::AlterTableAction::ForceRls(node) => build_keyword_tokens([ + (node.force_token(), "force"), + (node.row_token(), "row"), + (node.level_token(), "level"), + (node.security_token(), "security"), + ]), + ast::AlterTableAction::NoForceRls(node) => build_keyword_tokens([ + (node.no_token(), "no"), + (node.force_token(), "force"), + (node.row_token(), "row"), + (node.level_token(), "level"), + (node.security_token(), "security"), + ]), + ast::AlterTableAction::DisableRule(node) => append_commented_name( + build_keyword_tokens([ + (node.disable_token(), "disable"), + (node.rule_token(), "rule"), + ]), + node.rule_ref(), + ), + ast::AlterTableAction::EnableRule(node) => append_commented_name( + build_keyword_tokens([(node.enable_token(), "enable"), (node.rule_token(), "rule")]), + node.rule_ref(), + ), + ast::AlterTableAction::EnableAlwaysRule(node) => append_commented_name( + build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.always_token(), "always"), + (node.rule_token(), "rule"), + ]), + node.rule_ref(), + ), + ast::AlterTableAction::EnableReplicaRule(node) => append_commented_name( + build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.replica_token(), "replica"), + (node.rule_token(), "rule"), + ]), + node.rule_ref(), + ), + ast::AlterTableAction::DisableTrigger(node) => build_trigger_toggle( + build_keyword_tokens([ + (node.disable_token(), "disable"), + (node.trigger_token(), "trigger"), + ]), + node.trigger_target(), + ), + ast::AlterTableAction::EnableTrigger(node) => build_trigger_toggle( + build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.trigger_token(), "trigger"), + ]), + node.trigger_target(), + ), + ast::AlterTableAction::EnableAlwaysTrigger(node) => append_commented_name( + build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.always_token(), "always"), + (node.trigger_token(), "trigger"), + ]), + node.trigger_ref(), + ), + ast::AlterTableAction::EnableReplicaTrigger(node) => append_commented_name( + build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.replica_token(), "replica"), + (node.trigger_token(), "trigger"), + ]), + node.trigger_ref(), + ), + ast::AlterTableAction::DropColumn(node) => { + let mut doc = build_keyword_tokens([ + (node.drop_token(), "drop"), + (node.column_token(), "column"), + ]); + if let Some(if_exists) = node.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + doc = append_commented_name(doc, node.column_name_ref()); + if let Some(behavior) = node.drop_behavior() { + doc = doc + .append(Doc::space()) + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)); + } + doc + } + ast::AlterTableAction::DropConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.drop_token(), "drop"), + (node.constraint_token(), "constraint"), + ]); + if let Some(if_exists) = node.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + doc = append_commented_constraint_ref(doc, node.constraint_name_ref()); + if let Some(behavior) = node.drop_behavior() { + doc = doc + .append(Doc::space()) + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)); + } + doc + } + ast::AlterTableAction::InheritTable(node) => { + append_commented_table_ref(Doc::text("inherit"), node.table_name_ref()) + } + ast::AlterTableAction::NoInheritTable(node) => append_commented_table_ref( + build_keyword_tokens([(node.no_token(), "no"), (node.inherit_token(), "inherit")]), + node.table_name_ref(), + ), + ast::AlterTableAction::NotOf(node) => { + build_keyword_tokens([(node.not_token(), "not"), (node.of_token(), "of")]) + } + ast::AlterTableAction::OfType(node) => { + let mut doc = Doc::text("of"); + if let Some(ty) = node.ty() { + doc = doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + doc + } + ast::AlterTableAction::OptionItemList(node) => build_option_item_list(node), + ast::AlterTableAction::OwnerTo(node) => build_owner_to_role(&node), + ast::AlterTableAction::RenameColumn(node) => { + let mut doc = build_keyword_tokens([ + (node.rename_token(), "rename"), + (node.column_token(), "column"), + ]); + doc = append_commented_name(doc, node.column_name_ref()); + if let Some(to) = node.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + append_commented_name(doc, node.column_name()) + } + ast::AlterTableAction::RenameConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.rename_token(), "rename"), + (node.constraint_token(), "constraint"), + ]); + doc = append_commented_constraint_ref(doc, node.constraint_name_ref()); + if let Some(to) = node.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + append_commented_name(doc, node.constraint_name()) + } + ast::AlterTableAction::ReplicaIdentity(node) => build_replica_identity(node), + ast::AlterTableAction::ResetOptions(node) => { + let mut doc = Doc::text("reset"); + if let Some(list) = node.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_attribute_list(&list)); + } + doc + } + ast::AlterTableAction::SetAccessMethod(node) => { + let mut doc = build_keyword_tokens([ + (node.set_token(), "set"), + (node.access_token(), "access"), + (node.method_token(), "method"), + ]); + if let Some(method) = node.access_method_ref() { + doc = append_commented_name(doc, Some(method)); + } else if let Some(default) = node.default_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } + doc + } + ast::AlterTableAction::SetLogged(node) => { + build_keyword_tokens([(node.set_token(), "set"), (node.logged_token(), "logged")]) + } + ast::AlterTableAction::SetOptions(node) => build_set_options(&node), + ast::AlterTableAction::SetSchema(node) => build_set_schema(&node), + ast::AlterTableAction::SetTablespace(node) => build_set_tablespace(node), + ast::AlterTableAction::SetUnlogged(node) => build_keyword_tokens([ + (node.set_token(), "set"), + (node.unlogged_token(), "unlogged"), + ]), + ast::AlterTableAction::SetWithoutCluster(node) => build_keyword_tokens([ + (node.set_token(), "set"), + (node.without_token(), "without"), + (node.cluster_token(), "cluster"), + ]), + ast::AlterTableAction::SetWithoutOids(node) => build_keyword_tokens([ + (node.set_token(), "set"), + (node.without_token(), "without"), + (node.oids_token(), "oids"), + ]), + ast::AlterTableAction::TableRenameTo(node) => { + let target = node + .table_name() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); + build_rename_to(node.to_token(), target) + } + ast::AlterTableAction::ValidateConstraint(node) => append_commented_constraint_ref( + build_keyword_tokens([ + (node.validate_token(), "validate"), + (node.constraint_token(), "constraint"), + ]), + node.constraint_name_ref(), + ), + ast::AlterTableAction::MergePartitions(node) => { + let mut doc = build_keyword_tokens([ + (node.merge_token(), "merge"), + (node.partitions_token(), "partitions"), + ]); + if let Some(list) = node.table_name_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(build_table_name_ref_list(list)) + .nest(2), + ); + } + if let Some(into) = node.into_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&into)) + .append(Doc::text("into")) + .nest(2), + ); + } + doc = append_commented_name(doc, node.table_name()); + doc.group() + } + ast::AlterTableAction::SplitPartition(node) => { + let mut doc = build_keyword_tokens([ + (node.split_token(), "split"), + (node.partition_token(), "partition"), + (node.into_token(), "into"), + ]); + if let Some(list) = node.partition_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(build_partition_list(list)) + .nest(2), + ); + } + doc.group() + } + } +} + +fn build_alter_column_option<'a>(option: ast::AlterColumnOption) -> Doc<'a> { + match option { + ast::AlterColumnOption::DropDefault(n) => { + build_keyword_tokens([(n.drop_token(), "drop"), (n.default_token(), "default")]) + } + ast::AlterColumnOption::DropNotNull(n) => build_keyword_tokens([ + (n.drop_token(), "drop"), + (n.not_token(), "not"), + (n.null_token(), "null"), + ]), + ast::AlterColumnOption::SetNotNull(n) => build_keyword_tokens([ + (n.set_token(), "set"), + (n.not_token(), "not"), + (n.null_token(), "null"), + ]), + ast::AlterColumnOption::Inherit(n) => { + build_keyword_tokens([(n.inherit_token(), "inherit")]) + } + ast::AlterColumnOption::NoInherit(n) => { + build_keyword_tokens([(n.no_token(), "no"), (n.inherit_token(), "inherit")]) + } + ast::AlterColumnOption::DropExpression(n) => { + let mut doc = build_keyword_tokens([ + (n.drop_token(), "drop"), + (n.expression_token(), "expression"), + ]); + if let Some(x) = n.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_if_exists(x)); + } + doc + } + ast::AlterColumnOption::DropIdentity(n) => { + let mut doc = + build_keyword_tokens([(n.drop_token(), "drop"), (n.identity_token(), "identity")]); + if let Some(x) = n.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_if_exists(x)); + } + doc + } + ast::AlterColumnOption::ResetOptions(n) => { + let mut doc = Doc::text("reset"); + if let Some(list) = n.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_attribute_list(&list)); + } + doc + } + ast::AlterColumnOption::SetOptions(n) => build_set_options(&n), + ast::AlterColumnOption::SetOptionsList(n) => { + let mut doc = + build_keyword_tokens([(n.set_token(), "set"), (n.options_token(), "options")]); + if let Some(list) = n.alter_option_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_alter_option_list(&list)); + } + doc + } + ast::AlterColumnOption::SetDefault(n) => { + let mut doc = + build_keyword_tokens([(n.set_token(), "set"), (n.default_token(), "default")]); + if let Some(expr) = n.expr() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)) + .nest(2), + ); + } + doc.group() + } + ast::AlterColumnOption::SetStatistics(n) => { + let mut doc = build_keyword_tokens([ + (n.set_token(), "set"), + (n.statistics_token(), "statistics"), + ]); + if let Some(default) = n.default_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } else if let Some(expr) = n.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + doc + } + ast::AlterColumnOption::SetCompression(n) => append_commented_name( + build_keyword_tokens([ + (n.set_token(), "set"), + (n.compression_token(), "compression"), + ]), + n.compression_method_name(), + ), + ast::AlterColumnOption::SetStorage(n) => { + let mut doc = + build_keyword_tokens([(n.set_token(), "set"), (n.storage_token(), "storage")]); + if let Some(mode) = n.storage_mode() { + doc = doc + .append(Doc::space()) + .append(leading_comments(mode.syntax())) + .append(if mode.ident_token().is_some() { + build_name(mode.syntax()) + } else { + build_keyword_tokens([ + (mode.default_token(), "default"), + (mode.external_token(), "external"), + ]) + }); + } + doc + } + ast::AlterColumnOption::Restart(n) => { + let mut doc = Doc::text("restart"); + if let Some(with) = n.with_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + append_optional_literal(doc, n.literal()) + } + ast::AlterColumnOption::SetGenerated(n) => { + let mut doc = + build_keyword_tokens([(n.set_token(), "set"), (n.generated_token(), "generated")]); + if let Some(when) = n.generated_when() { + doc = doc + .append(Doc::space()) + .append(leading_comments(when.syntax())) + .append(build_generated_when(when)); + } + doc + } + ast::AlterColumnOption::SetSequenceOption(n) => { + let mut doc = Doc::text("set"); + if let Some(x) = n.sequence_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_sequence_option(x)); + } + doc + } + ast::AlterColumnOption::AddGenerated(n) => { + let mut doc = + build_keyword_tokens([(n.add_token(), "add"), (n.generated_token(), "generated")]); + if let Some(when) = n.generated_when() { + doc = doc + .append(Doc::space()) + .append(leading_comments(when.syntax())) + .append(build_generated_when(when)); + } + doc = doc.append(Doc::space()).append( + build_keyword_tokens([(n.as_token(), "as"), (n.identity_token(), "identity")]) + .nest(2), + ); + if let Some(list) = n.sequence_option_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_sequence_option_list(list)); + } + doc + } + ast::AlterColumnOption::SetExpression(n) => { + let mut doc = build_keyword_tokens([ + (n.set_token(), "set"), + (n.expression_token(), "expression"), + (n.as_token(), "as"), + ]); + if let Some(l) = n.l_paren_token() { + doc = doc.append(comments_before(l)); + } + let mut body = n + .expr() + .map(|e| leading_comments(e.syntax()).append(build_expr(e))) + .unwrap_or_else(Doc::nil); + if let Some(r) = n.r_paren_token() { + body = body.append(comments_before(r)); + } + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() + } + ast::AlterColumnOption::SetType(n) => { + let mut doc = build_keyword_tokens([ + (n.set_token(), "set"), + (n.data_token(), "data"), + (n.type_token(), "type"), + ]); + if let Some(ty) = n.ty() { + doc = doc + .append(Doc::space()) + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); + } + if let Some(collate) = n.collate() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(collate.syntax())) + .append(build_collate_expr(collate)) + .nest(2), + ); + } + if let Some(using) = n.using_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&using)) + .append(Doc::text("using")) + .nest(2), + ); + } + if let Some(expr) = n.expr() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)) + .nest(2), + ); + } + doc.group() + } + ast::AlterColumnOption::SetGeneratedOptions(n) => { + let mut doc = Doc::nil(); + for x in n.set_generated_options() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(x.syntax())) + .append(build_set_generated_option(x)) + .nest(2), + ); + } + doc.group() + } + } +} + +fn build_set_generated_option<'a>(option: ast::SetGeneratedOption) -> Doc<'a> { + match option { + ast::SetGeneratedOption::Restart(n) => { + let mut doc = Doc::text("restart"); + if let Some(with) = n.with_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + append_optional_literal(doc, n.literal()) + } + ast::SetGeneratedOption::SetGenerated(n) => { + let mut doc = + build_keyword_tokens([(n.set_token(), "set"), (n.generated_token(), "generated")]); + if let Some(when) = n.generated_when() { + doc = doc + .append(Doc::space()) + .append(leading_comments(when.syntax())) + .append(build_generated_when(when)); + } + doc + } + ast::SetGeneratedOption::SetSequenceOption(n) => { + let mut doc = Doc::text("set"); + if let Some(x) = n.sequence_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_sequence_option(x)); + } + doc + } + } +} + +fn build_table_name_ref_list<'a>(list: ast::TableNameRefList) -> Doc<'a> { + let items = list.table_name_refs().map(|table| { + let syntax = table.syntax().clone(); + (build_extension_path_value(&table), syntax) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r) = list.r_paren_token() { + body = body.append(comments_before(r)); + } + list.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() +} + +fn build_partition_list<'a>(list: ast::PartitionList) -> Doc<'a> { + let items = list.partitions().map(|partition| { + let syntax = partition.syntax().clone(); + let mut doc = build_keyword_tokens([(partition.partition_token(), "partition")]); + doc = append_commented_table_ref(doc, partition.table_name_ref()); + if let Some(kind) = partition.partition_type() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(kind.syntax())) + .append(build_create_table_partition_type(kind)) + .nest(2), + ); + } + (leading_comments(&syntax).append(doc.group()), syntax) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r) = list.r_paren_token() { + body = body.append(comments_before(r)); + } + list.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() +} + +fn build_trigger_toggle<'a>(mut doc: Doc<'a>, target: Option) -> Doc<'a> { + if let Some(target) = target { + doc = doc + .append(Doc::space()) + .append(leading_comments(target.syntax())) + .append(match target { + ast::TriggerTarget::All(node) => build_keyword_tokens([(node.all_token(), "all")]), + ast::TriggerTarget::User(node) => { + build_keyword_tokens([(node.user_token(), "user")]) + } + ast::TriggerTarget::TriggerRef(node) => build_name(node.syntax()), + }); + } + doc +} + +fn build_replica_identity<'a>(node: ast::ReplicaIdentity) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (node.replica_token(), "replica"), + (node.identity_token(), "identity"), + ]); + if let Some(option) = node.replica_identity_option() { + doc = doc + .append(Doc::space()) + .append(leading_comments(option.syntax())) + .append(match option { + ast::ReplicaIdentityOption::ReplicaIdentityDefault(n) => { + build_keyword_tokens([(n.default_token(), "default")]) + } + ast::ReplicaIdentityOption::ReplicaIdentityFull(n) => { + build_keyword_tokens([(n.full_token(), "full")]) + } + ast::ReplicaIdentityOption::ReplicaIdentityNothing(n) => { + build_keyword_tokens([(n.nothing_token(), "nothing")]) + } + ast::ReplicaIdentityOption::UsingIndexName(n) => build_using_index_name(n), + }); + } + doc +} + +fn build_alter_foreign_data_wrapper<'a>(stmt: &ast::AlterForeignDataWrapper) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.foreign_token(), "foreign"), + (stmt.data_token(), "data"), + (stmt.wrapper_token(), "wrapper"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(name) = stmt.foreign_data_wrapper_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(action) = stmt.action() { + let action_syntax = action.syntax().clone(); + let action_doc = match action { + ast::AlterForeignDataWrapperAction::FdwOptionList(list) => build_fdw_option_list(list), + ast::AlterForeignDataWrapperAction::ForeignDataWrapperRenameTo(rename) => { + let target = rename + .foreign_data_wrapper() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); + build_rename_to(rename.to_token(), target) + } + ast::AlterForeignDataWrapperAction::OwnerTo(owner) => build_owner_to_role(&owner), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&action_syntax)) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_fdw_option_list<'a>(list: ast::FdwOptionList) -> Doc<'a> { + let mut doc = Doc::nil(); + for (index, option) in list.fdw_options().enumerate() { + let option_doc = leading_comments(option.syntax()).append(build_fdw_option(option)); + doc = if index == 0 { + option_doc + } else { + doc.append(Doc::line_or_space().append(option_doc).nest(2)) + }; + } + doc.group() +} + +fn build_fdw_option<'a>(option: ast::FdwOption) -> Doc<'a> { + match option { + ast::FdwOption::OptionAlterOptionList(node) => { + let mut doc = Doc::text("options"); + let items = node.alter_options().map(|item| { + let syntax = item.syntax().clone(); + ( + leading_comments(&syntax).append(build_alter_option(&item)), + syntax, + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r_paren) = node.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + if let Some(l_paren) = node.l_paren_token() { + doc = doc.append(space_before_l_paren(l_paren)); + } + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() + } + ast::FdwOption::OptionConnection(node) => { + build_fdw_function_option("connection", node.function_name_ref()) + } + ast::FdwOption::OptionHandler(node) => { + build_fdw_function_option("handler", node.function_name_ref()) + } + ast::FdwOption::OptionValidator(node) => { + build_fdw_function_option("validator", node.function_name_ref()) + } + ast::FdwOption::OptionNoConnection(node) => build_keyword_tokens([ + (node.no_token(), "no"), + (node.connection_token(), "connection"), + ]), + ast::FdwOption::OptionNoHandler(node) => { + build_keyword_tokens([(node.no_token(), "no"), (node.handler_token(), "handler")]) + } + ast::FdwOption::OptionNoValidator(node) => build_keyword_tokens([ + (node.no_token(), "no"), + (node.validator_token(), "validator"), + ]), + } +} + +fn build_fdw_function_option<'a>( + keyword: &'static str, + function: Option, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(function) = function { + doc = doc + .append(Doc::space()) + .append(leading_comments(function.syntax())); + if let Some(path) = function.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc +} + +fn build_alter_function<'a>(stmt: &ast::AlterFunction) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.function_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("function")); + } + if let Some(sig) = stmt.function_sig() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sig.syntax())) + .append(build_function_sig(sig)); + } + if let Some(action) = stmt.action() { + let action_syntax = action.syntax().clone(); + let action_doc = match action { + ast::AlterFunctionAction::DependsOnExtension(node) => build_depends_on_extension( + false, + node.depends_token(), + node.on_token(), + node.extension_token(), + node.extension_ref(), + ), + ast::AlterFunctionAction::NoDependsOnExtension(node) => build_depends_on_extension( + true, + node.depends_token(), + node.on_token(), + node.extension_token(), + node.extension_ref(), + ), + ast::AlterFunctionAction::FuncOptionList(list) => build_alter_function_options(&list), + ast::AlterFunctionAction::FunctionRenameTo(node) => { + let target = node.function_name().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(node.to_token(), target) + } + ast::AlterFunctionAction::OwnerTo(node) => build_owner_to_role(&node), + ast::AlterFunctionAction::SetSchema(node) => build_set_schema(&node), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&action_syntax)) + .append(action_doc) + .nest(2), + ); + } + if let Some(token) = stmt.restrict_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&token)) + .append(Doc::text("restrict")) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_procedure<'a>(stmt: &ast::AlterProcedure) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.procedure_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("procedure")); + } + if let Some(sig) = stmt.procedure_sig() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sig.syntax())) + .append(build_procedure_sig(sig)); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterProcedureAction::DependsOnExtension(n) => build_depends_on_extension( + false, + n.depends_token(), + n.on_token(), + n.extension_token(), + n.extension_ref(), + ), + ast::AlterProcedureAction::NoDependsOnExtension(n) => build_depends_on_extension( + true, + n.depends_token(), + n.on_token(), + n.extension_token(), + n.extension_ref(), + ), + ast::AlterProcedureAction::FuncOptionList(n) => build_alter_function_options(n), + ast::AlterProcedureAction::OwnerTo(n) => build_owner_to_role(n), + ast::AlterProcedureAction::ProcedureRenameTo(n) => { + let target = n.procedure_name().map(|x| { + leading_comments(x.syntax()) + .append(x.path().map(|p| build_path(&p)).unwrap_or_else(Doc::nil)) + }); + build_rename_to(n.to_token(), target) + } + ast::AlterProcedureAction::SetSchema(n) => build_set_schema(n), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + append_restrict(doc, stmt.restrict_token()) + .group() + .append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_routine<'a>(stmt: &ast::AlterRoutine) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.routine_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("routine")); + } + if let Some(sig) = stmt.routine_sig() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sig.syntax())) + .append(build_routine_sig(sig)); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterRoutineAction::DependsOnExtension(n) => build_depends_on_extension( + false, + n.depends_token(), + n.on_token(), + n.extension_token(), + n.extension_ref(), + ), + ast::AlterRoutineAction::NoDependsOnExtension(n) => build_depends_on_extension( + true, + n.depends_token(), + n.on_token(), + n.extension_token(), + n.extension_ref(), + ), + ast::AlterRoutineAction::FuncOptionList(n) => build_alter_function_options(n), + ast::AlterRoutineAction::OwnerTo(n) => build_owner_to_role(n), + ast::AlterRoutineAction::RoutineRenameTo(n) => { + let target = n.routine_name().map(|x| { + leading_comments(x.syntax()) + .append(x.path().map(|p| build_path(&p)).unwrap_or_else(Doc::nil)) + }); + build_rename_to(n.to_token(), target) + } + ast::AlterRoutineAction::SetSchema(n) => build_set_schema(n), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + append_restrict(doc, stmt.restrict_token()) + .group() + .append(build_semicolon(stmt.semicolon_token())) +} + +fn append_restrict<'a>(mut doc: Doc<'a>, token: Option) -> Doc<'a> { + if let Some(token) = token { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&token)) + .append(Doc::text("restrict")) + .nest(2), + ); + } + doc +} + +fn build_alter_rule<'a>(stmt: &ast::AlterRule) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.rule_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("rule")); + } + if let Some(rule) = stmt.rule_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(rule.syntax())) + .append(build_name(rule.syntax())); + } + if let Some(on) = stmt.on_relation() { + let mut part = Doc::text("on"); + if let Some(relation) = on.relation_name_ref() { + part = part + .append(Doc::space()) + .append(leading_comments(relation.syntax())); + if let Some(path) = relation.path_ref() { + part = part.append(build_path_ref(&path)); + } + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(part) + .nest(2), + ); + } + if let Some(rename) = stmt.rule_rename_to() { + let target = rename + .rule() + .map(|rule| leading_comments(rule.syntax()).append(build_name(rule.syntax()))); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(rename.syntax())) + .append(build_rename_to(rename.to_token(), target)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_schema<'a>(stmt: &ast::AlterSchema) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.schema_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("schema")); + } + if let Some(schema) = stmt.schema_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(schema.syntax())) + .append(build_name(schema.syntax())); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterSchemaAction::OwnerTo(owner) => build_owner_to_role(owner), + ast::AlterSchemaAction::SchemaRenameTo(rename) => { + let target = rename.schema().map(|schema| { + leading_comments(schema.syntax()).append(build_name(schema.syntax())) + }); + build_rename_to(rename.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_sequence<'a>(stmt: &ast::AlterSequence) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.sequence_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("sequence")); + } + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(sequence) = stmt.sequence_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(sequence.syntax())); + if let Some(path) = sequence.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + for action in stmt.actions() { + let part = leading_comments(action.syntax()); + let part = part.append(match action { + ast::AlterSequenceAction::OwnerTo(owner) => build_owner_to_role(&owner), + ast::AlterSequenceAction::SequenceRenameTo(rename) => { + let target = rename.sequence().map(|sequence| { + leading_comments(sequence.syntax()).append( + sequence + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(rename.to_token(), target) + } + ast::AlterSequenceAction::SetLogged(action) => build_keyword_tokens([ + (action.set_token(), "set"), + (action.logged_token(), "logged"), + ]), + ast::AlterSequenceAction::SetSchema(action) => build_set_schema(&action), + ast::AlterSequenceAction::SetUnlogged(action) => build_keyword_tokens([ + (action.set_token(), "set"), + (action.unlogged_token(), "unlogged"), + ]), + ast::AlterSequenceAction::SequenceOption(option) => build_sequence_option(option), + }); + doc = doc.append(Doc::line_or_space().append(part).nest(2)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_server<'a>(stmt: &ast::AlterServer) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.server_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("server")); + } + if let Some(server) = stmt.server_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(server.syntax())) + .append(build_name(server.syntax())); + } + for action in stmt.actions() { + let action_doc = match &action { + ast::AlterServerAction::AlterOptionList(options) => build_alter_option_list(options), + ast::AlterServerAction::OwnerTo(owner) => build_owner_to_role(owner), + ast::AlterServerAction::ServerRenameTo(rename) => { + let target = rename.server().map(|server| { + leading_comments(server.syntax()).append(build_name(server.syntax())) + }); + build_rename_to(rename.to_token(), target) + } + ast::AlterServerAction::ServerVersion(version) => { + append_optional_literal(Doc::text("version"), version.literal()) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_statistics<'a>(stmt: &ast::AlterStatistics) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.statistics_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("statistics")); + } + if let Some(statistics) = stmt.statistics_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(statistics.syntax())); + if let Some(path) = statistics.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterStatisticsAction::OwnerTo(action) => build_owner_to_role(action), + ast::AlterStatisticsAction::SetSchema(action) => build_set_schema(action), + ast::AlterStatisticsAction::SetStatistics(action) => { + let mut action_doc = build_keyword_tokens([ + (action.set_token(), "set"), + (action.statistics_token(), "statistics"), + ]); + if let Some(default) = action.default_token() { + action_doc = action_doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } else if let Some(expr) = action.expr() { + action_doc = action_doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + action_doc + } + ast::AlterStatisticsAction::StatisticsRenameTo(action) => { + let target = action.statistics().map(|statistics| { + leading_comments(statistics.syntax()).append( + statistics + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_system<'a>(stmt: &ast::AlterSystem) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.system_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("system")); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterSystemAction::ResetConfigParam(action) => build_reset_config_param(action), + ast::AlterSystemAction::SetConfigParam(action) => build_set_config_param(action), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_table<'a>(stmt: &ast::AlterTable) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("table")); + } + if let Some(all) = stmt.all_in_tablespace() { + doc = doc + .append(Doc::space()) + .append(leading_comments(all.syntax())) + .append(build_all_in_tablespace(all)); + } else { + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(table) = stmt.table_relation_name() { + doc = doc + .append(Doc::space()) + .append(build_table_relation_name(table)); + } + let actions = stmt.actions().map(|action| { + let syntax = action.syntax().clone(); + ( + leading_comments(&syntax).append(build_alter_foreign_table_action(action)), + syntax, + ) + }); + if let Some(actions) = build_comma_separated_docs(actions) { + doc = doc.append(Doc::line_or_space().append(actions).nest(2)); + } + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_tablespace<'a>(stmt: &ast::AlterTablespace) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.tablespace_token() { + doc = doc .append(Doc::space()) - .append(leading_comments_token(&zone)) - .append(Doc::text("zone")); + .append(leading_comments_token(&token)) + .append(Doc::text("tablespace")); } - let value = if let Some(value) = target.config_value() { - let value_doc = match value.clone() { - ast::ConfigValue::ConfigValueName(name) => build_name(name.syntax()), - ast::ConfigValue::Literal(literal) => build_literal(literal), + if let Some(tablespace) = stmt.tablespace_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(tablespace.syntax())) + .append(build_name(tablespace.syntax())); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterTablespaceAction::OwnerTo(action) => build_owner_to_role(action), + ast::AlterTablespaceAction::ResetOptions(action) => { + let mut action_doc = Doc::text("reset"); + if let Some(attributes) = action.attribute_list() { + action_doc = action_doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + action_doc + } + ast::AlterTablespaceAction::SetOptions(action) => build_set_options(action), + ast::AlterTablespaceAction::TablespaceRenameTo(action) => { + let target = action.tablespace().map(|tablespace| { + leading_comments(tablespace.syntax()).append(build_name(tablespace.syntax())) + }); + build_rename_to(action.to_token(), target) + } }; - Some(leading_comments(value.syntax()).append(value_doc)) - } else if let Some(default) = target.default_token() { - Some(leading_comments_token(&default).append(Doc::text("default"))) - } else { - target - .local_token() - .map(|local| leading_comments_token(&local).append(Doc::text("local"))) - }; - if let Some(value) = value { - doc = doc.append(Doc::space()).append(value); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); } - doc.group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_set_config<'a>(set: ast::SetConfig) -> Doc<'a> { - let mut doc = Doc::nil(); - if let Some(parameter) = set.config_parameter_ref() { - doc = doc.append(leading_comments(parameter.syntax())); - if let Some(path) = parameter.path_ref() { +fn build_alter_text_search_configuration<'a>(stmt: &ast::AlterTextSearchConfiguration) -> Doc<'a> { + let mut doc = build_alter_text_search_header( + stmt.text_token(), + stmt.search_token(), + stmt.configuration_token(), + "configuration", + ); + if let Some(name) = stmt.text_search_configuration_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { doc = doc.append(build_path_ref(&path)); } } - if let Some(assignment) = set.config_assignment() { - let comments = leading_comments(assignment.syntax()); + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterTextSearchConfigurationAction::AddMapping(action) => { + build_add_mapping(action) + } + ast::AlterTextSearchConfigurationAction::AlterMapping(action) => { + build_alter_mapping(action) + } + ast::AlterTextSearchConfigurationAction::DropMapping(action) => { + build_drop_mapping(action) + } + ast::AlterTextSearchConfigurationAction::OwnerTo(action) => build_owner_to_role(action), + ast::AlterTextSearchConfigurationAction::SetSchema(action) => build_set_schema(action), + ast::AlterTextSearchConfigurationAction::TextSearchConfigurationRenameTo(action) => { + let target = action.text_search_configuration().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_text_search_dictionary<'a>(stmt: &ast::AlterTextSearchDictionary) -> Doc<'a> { + let mut doc = build_alter_text_search_header( + stmt.text_token(), + stmt.search_token(), + stmt.dictionary_token(), + "dictionary", + ); + if let Some(name) = stmt.text_search_dictionary_ref() { doc = doc - .append(Doc::line_or_space()) - .append(comments) - .append(build_config_assignment(assignment)); + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } } - doc.group() + if let Some(action) = stmt.action() { + let is_attribute_list = matches!( + &action, + ast::AlterTextSearchDictionaryAction::AttributeList(_) + ); + let action_doc = match &action { + ast::AlterTextSearchDictionaryAction::AttributeList(action) => { + build_attribute_list(action) + } + ast::AlterTextSearchDictionaryAction::OwnerTo(action) => build_owner_to_role(action), + ast::AlterTextSearchDictionaryAction::SetSchema(action) => build_set_schema(action), + ast::AlterTextSearchDictionaryAction::TextSearchDictionaryRenameTo(action) => { + let target = action.text_search_dictionary().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + }; + let action_doc = leading_comments(action.syntax()).append(action_doc); + doc = doc.append(if is_attribute_list { + Doc::space().append(action_doc) + } else { + Doc::line_or_space().append(action_doc).nest(2) + }); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_set_config_param<'a>(set: ast::SetConfigParam) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(parameter) = set.config_parameter_ref() { +fn build_alter_text_search_parser<'a>(stmt: &ast::AlterTextSearchParser) -> Doc<'a> { + let mut doc = build_alter_text_search_header( + stmt.text_token(), + stmt.search_token(), + stmt.parser_token(), + "parser", + ); + if let Some(name) = stmt.text_search_parser_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(parameter.syntax())); - if let Some(path) = parameter.path_ref() { + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterTextSearchParserAction::SetSchema(action) => build_set_schema(action), + ast::AlterTextSearchParserAction::TextSearchParserRenameTo(action) => { + let target = action.text_search_parser().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_text_search_template<'a>(stmt: &ast::AlterTextSearchTemplate) -> Doc<'a> { + let mut doc = build_alter_text_search_header( + stmt.text_token(), + stmt.search_token(), + stmt.template_token(), + "template", + ); + if let Some(name) = stmt.text_search_template_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterTextSearchTemplateAction::SetSchema(action) => build_set_schema(action), + ast::AlterTextSearchTemplateAction::TextSearchTemplateRenameTo(action) => { + let target = action.text_search_template().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_trigger<'a>(stmt: &ast::AlterTrigger) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(trigger) = stmt.trigger_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&trigger)) + .append(Doc::text("trigger")); + } + if let Some(name) = stmt.trigger_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(on) = stmt.on_relation() { + let mut on_doc = Doc::text("on"); + if let Some(relation) = on.relation_name_ref() { + on_doc = on_doc + .append(Doc::space()) + .append(leading_comments(relation.syntax())); + if let Some(path) = relation.path_ref() { + on_doc = on_doc.append(build_path_ref(&path)); + } + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterTriggerAction::DependsOnExtension(action) => build_depends_on_extension( + false, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ), + ast::AlterTriggerAction::NoDependsOnExtension(action) => build_depends_on_extension( + true, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ), + ast::AlterTriggerAction::TriggerRenameTo(action) => { + let target = action + .trigger() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_type<'a>(stmt: &ast::AlterType) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(type_token) = stmt.type_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&type_token)) + .append(Doc::text("type")); + } + if let Some(name) = stmt.type_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { doc = doc.append(build_path_ref(&path)); } } - if let Some(assignment) = set.config_assignment() { - let comments = leading_comments(assignment.syntax()); - doc = doc - .append(Doc::space()) - .append(comments) - .append(build_config_assignment(assignment)); - } - doc.group() + if let Some(action) = stmt.action() { + let syntax = action.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_alter_type_action(action)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_type_action<'a>(action: ast::AlterTypeAction) -> Doc<'a> { + match action { + ast::AlterTypeAction::AddValue(action) => { + let mut doc = build_keyword_tokens([ + (action.add_token(), "add"), + (action.value_token(), "value"), + ]); + if let Some(if_not_exists) = action.if_not_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_not_exists.syntax())) + .append(build_keyword_node(if_not_exists.syntax())); + } + if let Some(value) = action.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_literal(value)); + } + if let Some(position) = action.value_position() { + let syntax = position.syntax().clone(); + let position_doc = match position { + ast::ValuePosition::AfterValue(position) => append_optional_literal( + build_keyword_tokens([(position.after_token(), "after")]), + position.literal(), + ), + ast::ValuePosition::BeforeValue(position) => append_optional_literal( + build_keyword_tokens([(position.before_token(), "before")]), + position.literal(), + ), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(position_doc) + .nest(2), + ); + } + doc.group() + } + ast::AlterTypeAction::AlterTypeAttributeActionList(list) => { + let actions = list.actions().map(|action| { + let syntax = action.syntax().clone(); + ( + leading_comments(&syntax).append(build_alter_type_attribute_action(action)), + syntax, + ) + }); + build_comma_separated_docs(actions).unwrap_or_else(Doc::nil) + } + ast::AlterTypeAction::OwnerTo(action) => build_owner_to_role(&action), + ast::AlterTypeAction::RenameAttribute(action) => { + let mut doc = build_keyword_tokens([ + (action.rename_token(), "rename"), + (action.attribute_token(), "attribute"), + ]); + if let Some(name) = action.composite_field_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + if let Some(to) = action.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + if let Some(name) = action.composite_field() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + append_inline_drop_behavior(doc, action.drop_behavior()) + } + ast::AlterTypeAction::RenameValue(action) => { + let mut doc = build_keyword_tokens([ + (action.rename_token(), "rename"), + (action.value_token(), "value"), + ]); + if let Some(value) = action.from() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_literal(value)); + } + if let Some(to) = action.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + if let Some(value) = action.to() { + doc = doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_literal(value)); + } + doc + } + ast::AlterTypeAction::SetOptions(action) => build_set_options(&action), + ast::AlterTypeAction::SetSchema(action) => build_set_schema(&action), + ast::AlterTypeAction::TypeRenameTo(action) => { + let target = action.type_name().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + } } -fn build_config_assignment<'a>(assignment: ast::ConfigAssignment) -> Doc<'a> { - match assignment { - ast::ConfigAssignment::FromCurrent(current) => build_keyword_node(current.syntax()), - ast::ConfigAssignment::ToConfigValue(values) => { - let mut doc = if let Some(eq) = values.eq_token() { - leading_comments_token(&eq).append(Doc::text("=")) - } else if let Some(to) = values.to_token() { - leading_comments_token(&to).append(Doc::text("to")) - } else { - Doc::nil() - }; - let value_docs = values.config_values().map(|value| { - let syntax = value.syntax().clone(); - let value_doc = match value { - ast::ConfigValue::ConfigValueName(name) => build_name(name.syntax()), - ast::ConfigValue::Literal(literal) => build_literal(literal), - }; - (leading_comments(&syntax).append(value_doc), syntax) - }); - if let Some(values_doc) = build_comma_separated_docs(value_docs) { - doc = doc.append(Doc::space()).append(values_doc); - } else if let Some(default) = values.default_token() { +fn build_alter_type_attribute_action<'a>(action: ast::AlterTypeAttributeAction) -> Doc<'a> { + match action { + ast::AlterTypeAttributeAction::AddAttribute(action) => { + let mut doc = build_keyword_tokens([ + (action.add_token(), "add"), + (action.attribute_token(), "attribute"), + ]); + if let Some(name) = action.composite_field() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&default)) - .append(Doc::text("default")); - } else if let Some(null) = values.null_token() { + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + append_alter_type_attribute_tail( + doc, + action.ty(), + action.collate(), + action.drop_behavior(), + ) + } + ast::AlterTypeAttributeAction::AlterAttribute(action) => { + let mut doc = build_keyword_tokens([ + (action.alter_token(), "alter"), + (action.attribute_token(), "attribute"), + ]); + if let Some(name) = action.composite_field_ref() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&null)) - .append(Doc::text("null")); + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); } - doc.group() + for (token, keyword) in [ + (action.set_token(), "set"), + (action.data_token(), "data"), + (action.type_token(), "type"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + append_alter_type_attribute_tail( + doc, + action.ty(), + action.collate(), + action.drop_behavior(), + ) + } + ast::AlterTypeAttributeAction::DropAttribute(action) => { + let mut doc = build_keyword_tokens([ + (action.drop_token(), "drop"), + (action.attribute_token(), "attribute"), + ]); + if let Some(if_exists) = action.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(name) = action.composite_field_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + append_inline_drop_behavior(doc, action.drop_behavior()) } } } -fn build_create_index<'a>(create_index: &ast::CreateIndex) -> Doc<'a> { - let mut doc = Doc::text("create"); - - if let Some(unique_token) = create_index.unique_token() { +fn append_alter_type_attribute_tail<'a>( + mut doc: Doc<'a>, + ty: Option, + collate: Option, + behavior: Option, +) -> Doc<'a> { + if let Some(ty) = ty { doc = doc .append(Doc::space()) - .append(leading_comments_token(&unique_token)) - .append(Doc::text("unique")); + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); } - if let Some(index_token) = create_index.index_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&index_token)); - } else { - doc = doc.append(Doc::space()); + if let Some(collate) = collate { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(collate.syntax())) + .append(build_collate_expr(collate)) + .nest(2), + ); } - doc = doc.append(Doc::text("index")); - if let Some(concurrently_token) = create_index.concurrently_token() { + append_inline_drop_behavior(doc, behavior).group() +} + +fn append_inline_drop_behavior<'a>( + mut doc: Doc<'a>, + behavior: Option, +) -> Doc<'a> { + if let Some(behavior) = behavior { doc = doc .append(Doc::space()) - .append(leading_comments_token(&concurrently_token)) - .append(Doc::text("concurrently")); + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)); } - if let Some(if_not_exists) = create_index.if_not_exists() { + doc +} + +fn build_alter_user<'a>(stmt: &ast::AlterUser) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(user) = stmt.user_token() { doc = doc .append(Doc::space()) - .append(leading_comments(if_not_exists.syntax())) - .append(build_keyword_node(if_not_exists.syntax())); + .append(leading_comments_token(&user)) + .append(Doc::text("user")); } - if let Some(index) = create_index.index() { + if let Some(role) = stmt.role_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(index.syntax())); - if let Some(path) = index.path() { - doc = doc.append(build_path(&path)); - } - } - let mut on_doc = Doc::nil(); - if let Some(on_token) = create_index.on_token() { - on_doc = on_doc - .append(leading_comments_token(&on_token)) - .append(Doc::text("on")); - } - if let Some(table) = create_index.table_relation_name() { - on_doc = on_doc + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } else if let Some(all) = stmt.all_token() { + doc = doc .append(Doc::space()) - .append(build_table_relation_name(table)); - } - if let Some(using_method) = create_index.using_method() { - let mut using_doc = leading_comments(using_method.syntax()).append(Doc::text("using")); - if let Some(method) = using_method.access_method_ref() { - using_doc = using_doc - .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(build_name(method.syntax())); - } - if let Some(items) = create_index.partition_item_list() { - using_doc = using_doc - .append(Doc::space()) - .append(leading_comments(items.syntax())) - .append(build_create_table_partition_items(items)); - } - on_doc = on_doc.append(Doc::line_or_space()).append(using_doc); - doc = doc.append(Doc::hard_line().append(on_doc.group()).nest(2)); - } else { - if let Some(items) = create_index.partition_item_list() { - on_doc = on_doc - .append(Doc::space()) - .append(leading_comments(items.syntax())) - .append(build_create_table_partition_items(items)); - } - doc = doc.append(Doc::line_or_space().append(on_doc).nest(2)); + .append(leading_comments_token(&all)) + .append(Doc::text("all")); } - if let Some(include) = create_index.index_include_clause() { - let mut include_doc = leading_comments(include.syntax()).append(Doc::text("include")); - if let Some(items) = include.partition_item_list() { - include_doc = include_doc + if let Some(database) = stmt.in_database() { + let mut database_doc = build_keyword_tokens([ + (database.in_token(), "in"), + (database.database_token(), "database"), + ]); + if let Some(name) = database.database_ref() { + database_doc = database_doc .append(Doc::space()) - .append(leading_comments(items.syntax())) - .append(build_create_table_partition_items(items)); + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); } - doc = doc.append(Doc::line_or_space().append(include_doc).nest(2)); - } - if let Some(nulls) = create_index.nulls_distinct_option() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(nulls.syntax())) - .append(build_keyword_node(nulls.syntax())) + .append(leading_comments(database.syntax())) + .append(database_doc) .nest(2), ); } - if let Some(params) = create_index.with_params() { + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterUserAction::ResetConfigParam(action) => build_reset_config_param(action), + ast::AlterUserAction::RoleOptionList(action) => build_role_option_list(action), + ast::AlterUserAction::RoleRenameTo(action) => { + let target = action + .role() + .map(|role| leading_comments(role.syntax()).append(build_name(role.syntax()))); + build_rename_to(action.to_token(), target) + } + ast::AlterUserAction::SetConfigParam(action) => build_set_config_param(action), + }; doc = doc.append( Doc::line_or_space() - .append(leading_comments(params.syntax())) - .append(build_with_params(params)) + .append(leading_comments(action.syntax())) + .append(action_doc) .nest(2), ); } - if let Some(tablespace) = create_index.tablespace_clause() { - let mut tablespace_doc = - leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); - if let Some(tablespace_ref) = tablespace.tablespace_ref() { - tablespace_doc = tablespace_doc + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_user_mapping<'a>(stmt: &ast::AlterUserMapping) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.user_token(), "user"), + (stmt.mapping_token(), "mapping"), + (stmt.for_token(), "for"), + ] { + if let Some(token) = token { + doc = doc .append(Doc::space()) - .append(leading_comments(tablespace_ref.syntax())) - .append(build_name(tablespace_ref.syntax())); + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } - doc = doc.append(Doc::line_or_space().append(tablespace_doc).nest(2)); } - if let Some(where_clause) = create_index.where_clause() { + if let Some(role) = stmt.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + if let Some(server) = stmt.server_clause() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(where_clause.syntax())) - .append(build_where_clause(where_clause)) + .append(leading_comments(server.syntax())) + .append(build_server_clause(server)) .nest(2), ); } + if let Some(options) = stmt.alter_option_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_alter_option_list(&options)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_text_search_header<'a>( + text: Option, + search: Option, + object: Option, + object_keyword: &'static str, +) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [(text, "text"), (search, "search"), (object, object_keyword)] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + doc +} + +fn build_text_search_token_kinds<'a>( + kinds: impl Iterator, +) -> Option> { + build_comma_separated_docs(kinds.map(|kind| { + let syntax = kind.syntax().clone(); + ( + leading_comments(&syntax).append(build_name(&syntax)), + syntax, + ) + })) +} + +fn build_text_search_dictionaries<'a>( + dictionaries: impl Iterator, +) -> Option> { + build_comma_separated_docs(dictionaries.map(|dictionary| { + let syntax = dictionary.syntax().clone(); + (build_extension_path_value(&dictionary), syntax) + })) +} +fn build_add_mapping<'a>(action: &ast::AddMapping) -> Doc<'a> { + let mut doc = Doc::text("add"); + for (token, keyword) in [ + (action.mapping_token(), "mapping"), + (action.for_token(), "for"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(kinds) = build_text_search_token_kinds(action.text_search_token_kinds()) { + doc = doc.append(Doc::line_or_space().append(kinds).nest(2)); + } + if let Some(with) = action.with_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + } + if let Some(dictionaries) = build_text_search_dictionaries(action.text_search_dictionary_refs()) + { + doc = doc.append(Doc::line_or_space().append(dictionaries).nest(2)); + } doc.group() - .append(build_semicolon(create_index.semicolon_token())) } -fn build_create_view<'a>(create_view: &ast::CreateView) -> Doc<'a> { - let mut doc = Doc::text("create"); - - if let Some(or_replace) = create_view.or_replace() { +fn build_alter_mapping<'a>(action: &ast::AlterMapping) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(mapping) = action.mapping_token() { doc = doc .append(Doc::space()) - .append(leading_comments(or_replace.syntax())) - .append(build_keyword_node(or_replace.syntax())); + .append(leading_comments_token(&mapping)) + .append(Doc::text("mapping")); } - if let Some(persistence) = create_view.persistence() { + if let Some(for_token) = action.for_token() { doc = doc - .append(Doc::space()) - .append(leading_comments(persistence.syntax())) - .append(build_keyword_node(persistence.syntax())); + .append(Doc::line_or_space()) + .append(leading_comments_token(&for_token)) + .append(Doc::text("for")); } - if let Some(recursive) = create_view.recursive_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&recursive)) - .append(Doc::text("recursive")); + if let Some(kinds) = build_text_search_token_kinds(action.text_search_token_kinds()) { + doc = doc.append(Doc::line_or_space().append(kinds).nest(2)); } - if let Some(view_token) = create_view.view_token() { + if let Some(with) = action.with_token() { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&view_token)); - } else { - doc = doc.append(Doc::space()); + .append(Doc::line_or_space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); + if let Some(dictionaries) = + build_text_search_dictionaries(action.text_search_dictionary_refs()) + { + doc = doc.append(Doc::line_or_space().append(dictionaries).nest(2)); + } + } else if let Some(replace) = action.replace_dictionary() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(replace.syntax())) + .append(build_replace_dictionary(replace)); } - doc = doc.append(Doc::text("view")); + doc.group() +} - if let Some(view) = create_view.view() { +fn build_replace_dictionary<'a>(replace: ast::ReplaceDictionary) -> Doc<'a> { + let mut doc = Doc::text("replace"); + if let Some(before) = replace.before() { doc = doc .append(Doc::space()) - .append(leading_comments(view.syntax())); - if let Some(path) = view.path() { - doc = doc.append(build_path(&path)); + .append(leading_comments(before.syntax())); + if let Some(path) = before.path_ref() { + doc = doc.append(build_path_ref(&path)); } } - if let Some(columns) = create_view.column_list() { + if let Some(with) = replace.with_token() { doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_cte_column_list(columns)); - } - let has_with_params = if let Some(with_params) = create_view.with_params() { - doc = doc.append( - Doc::hard_line() - .append(leading_comments(with_params.syntax())) - .append(build_with_params(with_params)) - .nest(2), - ); - true - } else { - false - }; - if let Some(as_token) = create_view.as_token() { - let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); - doc = if has_with_params { - doc.append(Doc::hard_line().append(as_doc).nest(2)) - } else { - doc.append(Doc::space()).append(as_doc) - }; - } - if let Some(query) = create_view.query() { - doc = doc.append( - Doc::hard_line() - .append(leading_comments(query.syntax())) - .append(build_select_variant(query)) - .nest(2), - ); + .append(Doc::line_or_space()) + .append(leading_comments_token(&with)) + .append(Doc::text("with")); } - if let Some(check_option) = create_view.with_check_option() { - doc = doc.append( - Doc::hard_line() - .append(leading_comments(check_option.syntax())) - .append(build_with_check_option(check_option)) - .nest(2), - ); + if let Some(after) = replace.after() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(after.syntax())); + if let Some(path) = after.path_ref() { + doc = doc.append(build_path_ref(&path)); + } } - - doc.append(build_semicolon(create_view.semicolon_token())) - .group() + doc.group() } -fn build_create_table_as<'a>(create_table_as: &ast::CreateTableAs) -> Doc<'a> { - let mut doc = Doc::text("create"); - - if let Some(persistence) = create_table_as.persistence() { +fn build_drop_mapping<'a>(action: &ast::DropMapping) -> Doc<'a> { + let mut doc = Doc::text("drop"); + if let Some(mapping) = action.mapping_token() { doc = doc .append(Doc::space()) - .append(leading_comments(persistence.syntax())) - .append(build_keyword_node(persistence.syntax())); + .append(leading_comments_token(&mapping)) + .append(Doc::text("mapping")); } - if let Some(table_token) = create_table_as.table_token() { + if let Some(if_exists) = action.if_exists() { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&table_token)); - } else { - doc = doc.append(Doc::space()); + .append(Doc::line_or_space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc = doc.append(Doc::text("table")); + if let Some(for_token) = action.for_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&for_token)) + .append(Doc::text("for")); + } + if let Some(kinds) = build_text_search_token_kinds(action.text_search_token_kinds()) { + doc = doc.append(Doc::line_or_space().append(kinds).nest(2)); + } + doc.group() +} - if let Some(if_not_exists) = create_table_as.if_not_exists() { +fn build_alter_role<'a>(stmt: &ast::AlterRole) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.role_token() { doc = doc .append(Doc::space()) - .append(leading_comments(if_not_exists.syntax())) - .append(build_keyword_node(if_not_exists.syntax())); + .append(leading_comments_token(&token)) + .append(Doc::text("role")); } - if let Some(table_name) = create_table_as.table_name() { + if let Some(role) = stmt.role_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(table_name.syntax())); - if let Some(path) = table_name.path() { - doc = doc.append(build_path(&path)); - } - } - if let Some(arg_list) = create_table_as.table_arg_list() { - let comments = comments_before(arg_list.syntax().clone()); - if comment_tokens_before(arg_list.syntax().clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments).append(Doc::space()); - } - let body = Doc::list( - Itertools::intersperse( - arg_list.args().map(build_table_arg), - Doc::text(",").append(Doc::hard_line()), - ) - .collect(), - ); + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } else if let Some(all) = stmt.all_token() { doc = doc - .append(Doc::text("(")) - .append(wrap_body(body).group()) - .append(Doc::text(")")); - } - - let mut has_table_option = false; - if let Some(using_method) = create_table_as.using_method() { - has_table_option = true; - let mut option = leading_comments(using_method.syntax()).append(Doc::text("using")); - if let Some(method) = using_method.access_method_ref() { - option = option - .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(build_name(method.syntax())); - } - doc = doc.append(Doc::line_or_space().append(option).nest(2)); - } - if let Some(params) = create_table_as.table_params() { - has_table_option = true; - let option = match params { - ast::TableParams::WithParams(params) => { - leading_comments(params.syntax()).append(build_with_params(params)) - } - ast::TableParams::WithoutOids(without_oids) => leading_comments(without_oids.syntax()) - .append(build_keyword_node(without_oids.syntax())), - }; - doc = doc.append(Doc::line_or_space().append(option).nest(2)); - } - if let Some(on_commit) = create_table_as.on_commit() { - has_table_option = true; - let mut option = leading_comments(on_commit.syntax()).append(Doc::text("on")); - if let Some(commit_token) = on_commit.commit_token() { - option = option - .append(Doc::space()) - .append(leading_comments_token(&commit_token)); - } else { - option = option.append(Doc::space()); - } - option = option.append(Doc::text("commit")); - if let Some(action) = on_commit.on_commit_action() { - option = option - .append(Doc::space()) - .append(leading_comments(action.syntax())) - .append(build_keyword_node(action.syntax())); - } - doc = doc.append(Doc::line_or_space().append(option).nest(2)); + .append(Doc::space()) + .append(leading_comments_token(&all)) + .append(Doc::text("all")); } - if let Some(tablespace) = create_table_as.tablespace_clause() { - has_table_option = true; - let mut option = leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); - if let Some(name) = tablespace.tablespace_ref() { - option = option + if let Some(db) = stmt.in_database() { + let mut part = + build_keyword_tokens([(db.in_token(), "in"), (db.database_token(), "database")]); + if let Some(name) = db.database_ref() { + part = part .append(Doc::space()) .append(leading_comments(name.syntax())) .append(build_name(name.syntax())); } - doc = doc.append(Doc::line_or_space().append(option).nest(2)); - } - - if let Some(as_token) = create_table_as.as_token() { - let as_doc = leading_comments_token(&as_token).append(Doc::text("as")); - doc = if has_table_option { - doc.append(Doc::hard_line().append(as_doc).nest(2)) - } else { - doc.append(Doc::space()).append(as_doc) - }; - } - if let Some(query) = create_table_as.query() { - let query_comments = leading_comments(query.syntax()); - let query_doc = match query { - ast::CreateTableAsQuery::SelectVariant(select) => build_select_variant(select), - ast::CreateTableAsQuery::Execute(execute) => build_execute(execute), - }; doc = doc.append( - Doc::hard_line() - .append(query_comments) - .append(query_doc) + Doc::line_or_space() + .append(leading_comments(db.syntax())) + .append(part) .nest(2), ); } - if let Some(data_option) = create_table_as.data_option() { + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterRoleAction::ResetConfigParam(n) => build_reset_config_param(n), + ast::AlterRoleAction::SetConfigParam(n) => build_set_config_param(n), + ast::AlterRoleAction::RoleOptionList(n) => build_role_option_list(n), + ast::AlterRoleAction::RoleRenameTo(n) => { + let target = n + .role() + .map(|x| leading_comments(x.syntax()).append(build_name(x.syntax()))); + build_rename_to(n.to_token(), target) + } + }; doc = doc.append( - Doc::hard_line() - .append(leading_comments(data_option.syntax())) - .append(build_keyword_node(data_option.syntax())) + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) .nest(2), ); } - - doc.append(build_semicolon(create_table_as.semicolon_token())) - .group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_execute<'a>(execute: ast::Execute) -> Doc<'a> { - let mut doc = Doc::text("execute"); - if let Some(statement) = execute.prepared_statement_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(statement.syntax())) - .append(build_name(statement.syntax())); - } - if let Some(args) = execute.arg_list() { - doc = doc - .append(comments_before(args.syntax().clone())) - .append(build_call_arg_list(args)); +fn build_role_option_list<'a>(list: &ast::RoleOptionList) -> Doc<'a> { + let mut doc = list + .with_token() + .map(|t| leading_comments_token(&t).append(Doc::text("with"))) + .unwrap_or_else(Doc::nil); + for (i, option) in list.role_options().enumerate() { + let part = leading_comments(option.syntax()).append(build_role_option(option)); + doc = doc.append(if i == 0 { + Doc::space().append(part) + } else { + Doc::line_or_space().append(part) + }); } doc.group() - .append(build_semicolon(execute.semicolon_token())) } -fn build_with_check_option<'a>(check_option: ast::WithCheckOption) -> Doc<'a> { - let mut doc = Doc::text("with"); - if let Some(level) = check_option.check_option_level() { - let syntax = match &level { - ast::CheckOptionLevel::CascadedCheckOption(level) => level.syntax(), - ast::CheckOptionLevel::LocalCheckOption(level) => level.syntax(), - }; - doc = doc - .append(Doc::space()) - .append(leading_comments(syntax)) - .append(build_keyword_node(syntax)); +fn build_role_option<'a>(option: ast::RoleOption) -> Doc<'a> { + match option { + ast::RoleOption::RoleOptionGeneric(n) => build_name(n.syntax()), + ast::RoleOption::RoleOptionInherit(_) => Doc::text("inherit"), + ast::RoleOption::RoleOptionConnectionLimit(n) => append_literal( + build_keyword_tokens([ + (n.connection_token(), "connection"), + (n.limit_token(), "limit"), + ]), + n.literal(), + ), + ast::RoleOption::RoleOptionEncryptedPassword(n) => append_literal( + build_keyword_tokens([ + (n.encrypted_token(), "encrypted"), + (n.password_token(), "password"), + ]), + n.literal(), + ), + ast::RoleOption::RoleOptionPassword(n) => { + let doc = Doc::text("password"); + if let Some(x) = n.literal() { + append_literal(doc, Some(x)) + } else if let Some(t) = n.null_token() { + doc.append(Doc::space()) + .append(leading_comments_token(&t)) + .append(Doc::text("null")) + } else { + doc + } + } + ast::RoleOption::RoleOptionValidUntil(n) => append_literal( + build_keyword_tokens([(n.valid_token(), "valid"), (n.until_token(), "until")]), + n.literal(), + ), + ast::RoleOption::RoleOptionSysid(n) => append_literal(Doc::text("sysid"), n.literal()), + ast::RoleOption::RoleOptionAdmin(n) => { + append_role_list(Doc::text("admin"), n.role_ref_list()) + } + ast::RoleOption::RoleOptionInGroup(n) => append_role_list( + build_keyword_tokens([(n.in_token(), "in"), (n.group_token(), "group")]), + n.role_ref_list(), + ), + ast::RoleOption::RoleOptionInRole(n) => append_role_list( + build_keyword_tokens([(n.in_token(), "in"), (n.role_token(), "role")]), + n.role_ref_list(), + ), + ast::RoleOption::RoleOptionRole(n) => { + append_role_list(Doc::text("role"), n.role_ref_list()) + } + ast::RoleOption::RoleOptionUser(n) => { + append_role_list(Doc::text("user"), n.role_ref_list()) + } } - if let Some(check_token) = check_option.check_token() { +} + +fn append_literal<'a>(mut doc: Doc<'a>, literal: Option) -> Doc<'a> { + if let Some(x) = literal { doc = doc .append(Doc::space()) - .append(leading_comments_token(&check_token)) - .append(Doc::text("check")); + .append(leading_comments(x.syntax())) + .append(build_literal(x)); } - if let Some(option_token) = check_option.option_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&option_token)) - .append(Doc::text("option")); + doc +} +fn append_role_list<'a>(mut doc: Doc<'a>, roles: Option) -> Doc<'a> { + if let Some(x) = roles { + doc = doc.append( + Doc::space() + .append(leading_comments(x.syntax())) + .append(build_role_ref_list(x)) + .nest(2), + ); } doc } -fn build_create_foreign_table<'a>(create_table: &ast::CreateForeignTable) -> Doc<'a> { - let mut doc = Doc::text("create"); - - if let Some(foreign) = create_table.foreign_token() { +fn build_alter_property_graph<'a>(stmt: &ast::AlterPropertyGraph) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.property_token() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&foreign)) - .append(Doc::text("foreign")); + .append(leading_comments_token(&token)) + .append(Doc::text("property")); } - if let Some(table) = create_table.table_token() { + if let Some(token) = stmt.graph_token() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&table)) - .append(Doc::text("table")); + .append(leading_comments_token(&token)) + .append(Doc::text("graph")); } - if let Some(if_not_exists) = create_table.if_not_exists() { + if let Some(x) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(if_not_exists.syntax())) - .append(build_keyword_node(if_not_exists.syntax())); + .append(leading_comments(x.syntax())) + .append(build_if_exists(x)); } - if let Some(table_name) = create_table.table_name() { + if let Some(x) = stmt.property_graph_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(table_name.syntax())); - if let Some(path) = table_name.path() { - doc = doc.append(build_path(&path)); + .append(leading_comments(x.syntax())); + if let Some(path) = x.path_ref() { + doc = doc.append(build_path_ref(&path)); } } + if let Some(action) = stmt.action() { + let syntax = action.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_alter_property_graph_action(action)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} - if let Some(partition_of) = create_table.partition_of() { - let mut partition_doc = leading_comments(partition_of.syntax()) - .append(build_create_table_partition_of(partition_of)); - if let Some(arg_list) = create_table.table_arg_list() { - partition_doc = append_table_arg_list(partition_doc, arg_list); - } - if let Some(partition_type) = create_table.partition_type() { - let separator = if matches!(&partition_type, ast::PartitionType::PartitionDefault(_)) { - Doc::space() - } else { - Doc::line_or_space() - }; - partition_doc = partition_doc - .append(separator) - .append(leading_comments(partition_type.syntax())) - .append(build_create_table_partition_type(partition_type)); +fn build_alter_property_graph_action<'a>(action: ast::AlterPropertyGraphAction) -> Doc<'a> { + match action { + ast::AlterPropertyGraphAction::OwnerTo(n) => build_owner_to_role(&n), + ast::AlterPropertyGraphAction::SetSchema(n) => build_set_schema(&n), + ast::AlterPropertyGraphAction::PropertyGraphRenameTo(n) => { + let target = n.property_graph().map(|x| { + leading_comments(x.syntax()) + .append(x.path().map(|p| build_path(&p)).unwrap_or_else(Doc::nil)) + }); + build_rename_to(n.to_token(), target) } - doc = doc.append(Doc::hard_line().append(partition_doc).nest(2)); - } else { - if let Some(arg_list) = create_table.table_arg_list() { - doc = append_table_arg_list(doc, arg_list); + ast::AlterPropertyGraphAction::AddVertexEdgeTables(n) => { + let mut doc = Doc::text("add"); + if let Some(x) = n.vertex_tables() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_vertex_tables(x)); + } + if let Some(x) = n.edge_tables() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_edge_tables(x)); + } + doc } - if let Some(inherits) = create_table.inherits() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(inherits.syntax())) - .append(build_create_table_inherits(inherits)) - .nest(2), - ); + ast::AlterPropertyGraphAction::AlterVertexEdgeLabels(n) => build_alter_element_labels(n), + ast::AlterPropertyGraphAction::AddVertexEdgeLabelProperties(n) => { + build_add_element_properties(n) } - if let Some(partition_type) = create_table.partition_type() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(partition_type.syntax())) - .append(build_create_table_partition_type(partition_type)) - .nest(2), - ); + ast::AlterPropertyGraphAction::DropVertexEdgeLabel(n) => build_drop_element_label(n), + ast::AlterPropertyGraphAction::DropVertexEdgeLabelProperties(n) => { + build_drop_element_properties(n) } + ast::AlterPropertyGraphAction::DropVertexTables(n) => build_drop_element_tables( + build_keyword_tokens([ + (n.drop_token(), "drop"), + ( + n.vertex_token().or(n.node_token()), + if n.vertex_token().is_some() { + "vertex" + } else { + "node" + }, + ), + (n.tables_token(), "tables"), + ]), + n.element_table_refs(), + n.l_paren_token(), + n.r_paren_token(), + n.drop_behavior(), + ), + ast::AlterPropertyGraphAction::DropEdgeTables(n) => build_drop_element_tables( + build_keyword_tokens([ + (n.drop_token(), "drop"), + ( + n.edge_token().or(n.relationship_token()), + if n.edge_token().is_some() { + "edge" + } else { + "relationship" + }, + ), + (n.tables_token(), "tables"), + ]), + n.element_table_refs(), + n.l_paren_token(), + n.r_paren_token(), + n.drop_behavior(), + ), } - if let Some(server) = create_table.server_clause() { - doc = doc.append( - Doc::line_or_space() - .append(leading_comments(server.syntax())) - .append(build_server_clause(server)) - .nest(2), - ); +} + +fn build_element_kind<'a>( + vertex: Option, + node: Option, + edge: Option, + relationship: Option, +) -> Doc<'a> { + if let Some(t) = vertex { + leading_comments_token(&t).append(Doc::text("vertex")) + } else if let Some(t) = node { + leading_comments_token(&t).append(Doc::text("node")) + } else if let Some(t) = edge { + leading_comments_token(&t).append(Doc::text("edge")) + } else if let Some(t) = relationship { + leading_comments_token(&t).append(Doc::text("relationship")) + } else { + Doc::nil() } - if let Some(options) = create_table.alter_option_list() { +} +fn append_element_ref<'a>(mut doc: Doc<'a>, x: Option) -> Doc<'a> { + if let Some(x) = x { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_name(x.syntax())); + } + doc +} +fn append_label_ref<'a>(mut doc: Doc<'a>, x: Option) -> Doc<'a> { + if let Some(x) = x { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_name(x.syntax())); + } + doc +} +fn build_alter_element_labels<'a>(n: ast::AlterVertexEdgeLabels) -> Doc<'a> { + let mut doc = Doc::text("alter") + .append(Doc::space()) + .append(build_element_kind( + n.vertex_token(), + n.node_token(), + n.edge_token(), + n.relationship_token(), + )); + if let Some(t) = n.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&t)) + .append(Doc::text("table")); + } + doc = append_element_ref(doc, n.element_table_ref()); + for x in n.add_labels() { + let mut part = build_keyword_tokens([(x.add_token(), "add"), (x.label_token(), "label")]); + if let Some(label) = x.label() { + part = part + .append(Doc::space()) + .append(leading_comments(label.syntax())) + .append(build_name(label.syntax())); + } + if let Some(props) = x.element_table_properties() { + part = part + .append(Doc::space()) + .append(leading_comments(props.syntax())) + .append(build_element_properties(props)); + } doc = doc.append( Doc::line_or_space() - .append(leading_comments(options.syntax())) - .append(build_alter_option_list(options)) + .append(leading_comments(x.syntax())) + .append(part) .nest(2), ); } - doc.group() - .append(build_semicolon(create_table.semicolon_token())) } - -fn append_table_arg_list<'a>(mut doc: Doc<'a>, arg_list: ast::TableArgList) -> Doc<'a> { - doc = doc.append(leading_comments(arg_list.syntax())); - if let Some(l_paren) = arg_list.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); +fn build_add_element_properties<'a>(n: ast::AddVertexEdgeLabelProperties) -> Doc<'a> { + let mut doc = Doc::text("alter") + .append(Doc::space()) + .append(build_element_kind( + n.vertex_token(), + n.node_token(), + n.edge_token(), + n.relationship_token(), + )); + if let Some(t) = n.table_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&t)) + .append(Doc::text("table")); + } + doc = append_element_ref(doc, n.element_table_ref()); + for (token, keyword) in [(n.alter_token(), "alter"), (n.label_token(), "label")] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } } - let body = Doc::list( - Itertools::intersperse( - arg_list.args().map(build_table_arg), - Doc::text(",").append(Doc::hard_line()), - ) - .collect(), - ); - doc.append(Doc::text("(")) - .append(wrap_body(body).group()) - .append(Doc::text(")")) + doc = append_label_ref(doc, n.label_ref()); + for (token, keyword) in [(n.add_token(), "add"), (n.properties_token(), "properties")] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(x) = n.expr_as_property_name_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_expr_property_list(x)); + } + doc } - -fn build_server_clause<'a>(server: ast::ServerClause) -> Doc<'a> { - let mut doc = Doc::text("server"); - if let Some(server_ref) = server.server_ref() { +fn build_drop_element_label<'a>(n: ast::DropVertexEdgeLabel) -> Doc<'a> { + let mut doc = Doc::text("alter") + .append(Doc::space()) + .append(build_element_kind( + n.vertex_token(), + n.node_token(), + n.edge_token(), + n.relationship_token(), + )) + .append(Doc::space()) + .append(Doc::text("table")); + doc = append_element_ref(doc, n.element_table_ref()) + .append(Doc::space()) + .append(Doc::text("drop label")); + doc = append_label_ref(doc, n.label_ref()); + if let Some(x) = n.drop_behavior() { doc = doc .append(Doc::space()) - .append(leading_comments(server_ref.syntax())) - .append(build_name(server_ref.syntax())); + .append(leading_comments(x.syntax())) + .append(build_drop_behavior(x)); } doc } +fn build_drop_element_properties<'a>(n: ast::DropVertexEdgeLabelProperties) -> Doc<'a> { + let mut doc = Doc::text("alter") + .append(Doc::space()) + .append(build_element_kind( + n.vertex_token(), + n.node_token(), + n.edge_token(), + n.relationship_token(), + )) + .append(Doc::space()) + .append(Doc::text("table")); + doc = append_element_ref(doc, n.element_table_ref()); -fn build_create_table<'a>(create_table: &ast::CreateTable) -> Doc<'a> { - let mut doc = Doc::text("create"); - - if let Some(persistence) = create_table.persistence() { + let mut detail = build_keyword_tokens([(n.alter_token(), "alter"), (n.label_token(), "label")]); + detail = append_label_ref(detail, n.label_ref()); + for (token, keyword) in [ + (n.drop_token(), "drop"), + (n.properties_token(), "properties"), + ] { + if let Some(token) = token { + detail = detail + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + let items = n.property_name_refs().map(|x| { + ( + leading_comments(x.syntax()).append(build_name(x.syntax())), + x.syntax().clone(), + ) + }); + let body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + detail = detail + .append(Doc::space()) + .append( + n.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append( + n.r_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text(")")); + if let Some(x) = n.drop_behavior() { + detail = detail + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_drop_behavior(x)); + } + doc.append(Doc::line_or_space().append(detail).nest(2)) + .group() +} +fn build_drop_element_tables<'a>( + mut doc: Doc<'a>, + refs: impl Iterator, + l: Option, + r: Option, + behavior: Option, +) -> Doc<'a> { + let items = refs.map(|x| { + ( + leading_comments(x.syntax()).append(build_name(x.syntax())), + x.syntax().clone(), + ) + }); + let body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + doc = doc + .append(Doc::space()) + .append(l.map(comments_before).unwrap_or_else(Doc::nil)) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(r.map(comments_before).unwrap_or_else(Doc::nil)) + .append(Doc::text(")")); + if let Some(x) = behavior { doc = doc .append(Doc::space()) - .append(leading_comments(persistence.syntax())) - .append(build_keyword_node(persistence.syntax())); + .append(leading_comments(x.syntax())) + .append(build_drop_behavior(x)); } + doc +} - if let Some(table_token) = create_table.table_token() { +fn build_vertex_tables<'a>(n: ast::VertexTables) -> Doc<'a> { + let mut doc = build_element_kind(n.vertex_token(), n.node_token(), None, None); + if let Some(t) = n.tables_token() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&table_token)); - } else { - doc = doc.append(Doc::space()); + .append(leading_comments_token(&t)) + .append(Doc::text("tables")); + } + let items = n.vertex_table_defs().map(|x| { + ( + leading_comments(x.syntax()).append(build_vertex_table_def(x.clone())), + x.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r) = n.r_paren_token() { + body = body.append(comments_before(r)); + } + doc = doc + .append(Doc::space()) + .append( + n.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")); + doc.group() +} +fn build_vertex_table_def<'a>(n: ast::VertexTableDef) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(x) = n.table_name_ref() { + doc = doc.append(leading_comments(x.syntax())); + if let Some(p) = x.path_ref() { + doc = doc.append(build_path_ref(&p)); + } } - doc = doc.append(Doc::text("table")); - - if let Some(if_not_exists) = create_table.if_not_exists() { + if let Some(x) = n.element_table_alias_clause() { doc = doc .append(Doc::space()) - .append(leading_comments(if_not_exists.syntax())) - .append(build_keyword_node(if_not_exists.syntax())); + .append(leading_comments(x.syntax())) + .append(build_element_alias(x)); } - - if let Some(table_name) = create_table.table_name() { + if let Some(x) = n.element_table_key_clause() { doc = doc .append(Doc::space()) - .append(leading_comments(table_name.syntax())); - if let Some(path) = table_name.path() { - doc = doc.append(build_path(&path)); - } + .append(leading_comments(x.syntax())) + .append(build_element_key(x)); } - - if let Some(partition_of) = create_table.partition_of() { + if let Some(x) = n.element_table_label_and_properties() { doc = doc.append( - Doc::hard_line() - .append(leading_comments(partition_of.syntax())) - .append(build_create_table_partition_of(partition_of)) + Doc::line_or_space() + .append(leading_comments(x.syntax())) + .append(build_element_label_properties(x)) .nest(2), ); } - - if let Some(of_type) = create_table.of_type() { - let mut of_type_doc = leading_comments(of_type.syntax()).append(Doc::text("of")); - if let Some(ty) = of_type.ty() { - of_type_doc = of_type_doc - .append(Doc::space()) - .append(leading_comments(ty.syntax())) - .append(build_type(ty)); - } - doc = doc.append(Doc::hard_line().append(of_type_doc).nest(2)); + doc.group() +} +fn build_edge_tables<'a>(n: ast::EdgeTables) -> Doc<'a> { + let mut doc = build_element_kind(None, None, n.edge_token(), n.relationship_token()); + if let Some(t) = n.tables_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&t)) + .append(Doc::text("tables")); } - - if let Some(arg_list) = create_table.table_arg_list() { - if let Some(l_paren) = arg_list.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); - } + let items = n.edge_table_defs().map(|x| { + ( + leading_comments(x.syntax()).append(build_edge_table_def(x.clone())), + x.syntax().clone(), + ) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r) = n.r_paren_token() { + body = body.append(comments_before(r)); + } + doc = doc + .append(Doc::space()) + .append( + n.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")); + doc.group() +} +fn build_edge_table_def<'a>(n: ast::EdgeTableDef) -> Doc<'a> { + let mut doc = Doc::nil(); + if let Some(x) = n.table_name_ref() { + doc = doc.append(leading_comments(x.syntax())); + if let Some(p) = x.path_ref() { + doc = doc.append(build_path_ref(&p)); } - let body = Doc::list( - Itertools::intersperse( - arg_list.args().map(build_table_arg), - Doc::text(",").append(Doc::hard_line()), - ) - .collect(), - ); + } + if let Some(x) = n.element_table_alias_clause() { doc = doc - .append(Doc::text("(")) - .append(wrap_body(body).group()) - .append(Doc::text(")")); + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_element_alias(x)); } - - if let Some(partition_type) = create_table.partition_type() { - let separator = if matches!(&partition_type, ast::PartitionType::PartitionDefault(_)) { - Doc::space() - } else { - Doc::line_or_space() - }; + if let Some(x) = n.element_table_key_clause() { + doc = doc + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_element_key(x)); + } + if let Some(x) = n.source_vertex_table() { doc = doc.append( - separator - .append(leading_comments(partition_type.syntax())) - .append(build_create_table_partition_type(partition_type)) + Doc::line_or_space() + .append(leading_comments(x.syntax())) + .append(build_vertex_reference( + x.source_token(), + "source", + x.key_token(), + x.column_ref_list(), + x.references_table(), + )) .nest(2), ); } - - if let Some(inherits) = create_table.inherits() { + if let Some(x) = n.dest_vertex_table() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(inherits.syntax())) - .append(build_create_table_inherits(inherits)) + .append(leading_comments(x.syntax())) + .append(build_vertex_reference( + x.destination_token(), + "destination", + x.key_token(), + x.column_ref_list(), + x.references_table(), + )) .nest(2), ); } - - if let Some(partition_by) = create_table.partition_by() { + if let Some(x) = n.element_table_label_and_properties() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(partition_by.syntax())) - .append(build_create_table_partition_by(partition_by)) + .append(leading_comments(x.syntax())) + .append(build_element_label_properties(x)) .nest(2), ); } - - if let Some(using_method) = create_table.using_method() { - let mut using_doc = leading_comments(using_method.syntax()).append(Doc::text("using")); - if let Some(method) = using_method.access_method_ref() { - using_doc = using_doc - .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(build_name(method.syntax())); - } - doc = doc.append(Doc::line_or_space().append(using_doc).nest(2)); - } - - if let Some(params) = create_table.table_params() { - let (separator, params_doc) = match params { - ast::TableParams::WithParams(params) => ( - Doc::line_or_space(), - leading_comments(params.syntax()).append(build_with_params(params)), - ), - ast::TableParams::WithoutOids(without_oids) => ( - Doc::hard_line(), - leading_comments(without_oids.syntax()) - .append(build_keyword_node(without_oids.syntax())), - ), - }; - doc = doc.append(separator.append(params_doc).nest(2)); - } - - if let Some(on_commit) = create_table.on_commit() { - let mut on_commit_doc = leading_comments(on_commit.syntax()).append(Doc::text("on")); - if let Some(commit_token) = on_commit.commit_token() { - on_commit_doc = on_commit_doc - .append(Doc::space()) - .append(leading_comments_token(&commit_token)); - } else { - on_commit_doc = on_commit_doc.append(Doc::space()); - } - on_commit_doc = on_commit_doc.append(Doc::text("commit")); - if let Some(action) = on_commit.on_commit_action() { - on_commit_doc = on_commit_doc - .append(Doc::space()) - .append(leading_comments(action.syntax())) - .append(build_keyword_node(action.syntax())); - } - doc = doc.append(Doc::hard_line().append(on_commit_doc).nest(2)); - } - - if let Some(tablespace) = create_table.tablespace_clause() { - let mut tablespace_doc = - leading_comments(tablespace.syntax()).append(Doc::text("tablespace")); - if let Some(tablespace_ref) = tablespace.tablespace_ref() { - tablespace_doc = tablespace_doc - .append(Doc::space()) - .append(leading_comments(tablespace_ref.syntax())) - .append(build_name(tablespace_ref.syntax())); - } - doc = doc.append(Doc::line_or_space().append(tablespace_doc).nest(2)); - } - doc.group() - .append(build_semicolon(create_table.semicolon_token())) -} - -fn build_create_table_partition_of<'a>(partition_of: ast::PartitionOf) -> Doc<'a> { - let mut doc = Doc::text("partition"); - if let Some(of_token) = partition_of.of_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&of_token)); - } else { - doc = doc.append(Doc::space()); - } - doc = doc.append(Doc::text("of")); - if let Some(table) = partition_of.table_name_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(table.syntax())); - if let Some(path) = table.path_ref() { - doc = doc.append(build_path_ref(&path)); - } - } - doc } - -fn build_create_table_inherits<'a>(inherits: ast::Inherits) -> Doc<'a> { - let mut doc = Doc::text("inherits"); - if let Some(l_paren) = inherits.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); - } +fn build_element_alias<'a>(n: ast::ElementTableAliasClause) -> Doc<'a> { + let mut d = Doc::nil(); + if let Some(t) = n.as_token() { + d = d + .append(leading_comments_token(&t)) + .append(Doc::text("as")) + .append(Doc::space()); } - let tables = inherits.table_name_refs().map(|table| { - let mut item = leading_comments(table.syntax()); - if let Some(path) = table.path_ref() { - item = item.append(build_path_ref(&path)); - } - (item, table.syntax().clone()) - }); - let mut body = build_comma_separated_docs(tables).unwrap_or_else(Doc::nil); - if let Some(r_paren) = inherits.r_paren_token() { - body = body.append(comments_before(r_paren)); + if let Some(x) = n.alias() { + d = d + .append(leading_comments(x.syntax())) + .append(build_name(x.syntax())); } - doc.append(Doc::text("(")) - .append(wrap_body(body)) - .append(Doc::text(")")) - .group() + d } - -fn build_create_table_partition_by<'a>(partition_by: ast::PartitionBy) -> Doc<'a> { - let mut doc = Doc::text("partition"); - if let Some(by_token) = partition_by.by_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&by_token)); - } else { - doc = doc.append(Doc::space()); - } - doc = doc.append(Doc::text("by")); - if let Some(strategy) = partition_by.partition_strategy() { - doc = doc +fn build_element_key<'a>(n: ast::ElementTableKeyClause) -> Doc<'a> { + let mut d = n + .key_token() + .map(|t| leading_comments_token(&t).append(Doc::text("key"))) + .unwrap_or_else(Doc::nil); + if let Some(x) = n.column_ref_list() { + d = d .append(Doc::space()) - .append(leading_comments(strategy.syntax())) - .append(build_keyword_node(strategy.syntax())); + .append(leading_comments(x.syntax())) + .append(build_column_ref_list(x)); } - if let Some(items) = partition_by.partition_item_list() { - doc = doc + d +} +fn build_vertex_reference<'a>( + kind_token: Option, + keyword: &'static str, + key_token: Option, + cols: Option, + refs: Option, +) -> Doc<'a> { + let mut d = kind_token + .map(|token| leading_comments_token(&token).append(Doc::text(keyword))) + .unwrap_or_else(Doc::nil); + if let Some(token) = key_token { + d = d .append(Doc::space()) - .append(leading_comments(items.syntax())) - .append(build_create_table_partition_items(items)); + .append(leading_comments_token(&token)) + .append(Doc::text("key")); } - doc -} - -fn build_create_table_partition_items<'a>(items: ast::PartitionItemList) -> Doc<'a> { - let doc = items - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let item_docs = items.partition_items().map(|item| { - let mut item_doc = leading_comments(item.syntax()); - if let Some(expr) = item.expr() { - item_doc = item_doc.append(build_expr(expr)); - } - if item.expr().is_none() { - if let Some(collate) = item.collate() { - item_doc = item_doc.append(build_collate_expr(collate)); - } - } - if let Some(op_class) = item.op_class_ref() { - item_doc = item_doc - .append(Doc::space()) - .append(leading_comments(op_class.syntax())); - if let Some(path) = op_class.path_ref() { - item_doc = item_doc.append(build_path_ref(&path)); - } - } - if let Some(attributes) = item.attribute_list() { - item_doc = item_doc - .append(Doc::space()) - .append(leading_comments(attributes.syntax())) - .append(build_attribute_list(attributes)); + if let Some(x) = cols { + d = d + .append(Doc::space()) + .append(leading_comments(x.syntax())) + .append(build_column_ref_list(x)); + } + if let Some(x) = refs { + d = d.append(Doc::space()).append(leading_comments(x.syntax())); + if let Some(token) = x.references_token() { + d = d + .append(leading_comments_token(&token)) + .append(Doc::text("references")); } - if let Some(order) = item.sort_order() { - item_doc = item_doc + if let Some(v) = x.vertex_table_ref() { + d = d .append(Doc::space()) - .append(leading_comments(order.syntax())) - .append(build_keyword_node(order.syntax())); + .append(leading_comments(v.syntax())) + .append(build_name(v.syntax())); } - if let Some(nulls) = item.nulls_order() { - item_doc = item_doc + if let Some(c) = x.column_ref_list() { + d = d .append(Doc::space()) - .append(leading_comments(nulls.syntax())) - .append(build_keyword_node(nulls.syntax())); - } - (item_doc, item.syntax().clone()) - }); - let mut body = build_comma_separated_docs(item_docs).unwrap_or_else(Doc::nil); - if let Some(r_paren) = items.r_paren_token() { - body = body.append(comments_before(r_paren)); - } - doc.append(wrap_body(body)).append(Doc::text(")")).group() -} - -fn build_create_table_partition_type<'a>(partition_type: ast::PartitionType) -> Doc<'a> { - match partition_type { - ast::PartitionType::PartitionDefault(_) => Doc::text("default"), - ast::PartitionType::PartitionForValuesIn(values) => { - let mut doc = build_partition_for_values_prefix(values.values_token()); - if let Some(in_token) = values.in_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&in_token)); - } else { - doc = doc.append(Doc::space()); - } - doc = doc.append(Doc::text("in")); - if let Some(l_paren) = values.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); + .append(leading_comments(c.syntax())) + .append(build_column_ref_list(c)); + } + } + d +} +fn build_element_label_properties<'a>(x: ast::ElementTableLabelAndProperties) -> Doc<'a> { + match x { + ast::ElementTableLabelAndProperties::ElementTableProperties(p) => { + build_element_properties(p) + } + ast::ElementTableLabelAndProperties::LabelAndPropertiesList(list) => { + let mut d = Doc::nil(); + for (i, x) in list.label_and_propertiess().enumerate() { + let mut p = Doc::nil(); + if let Some(s) = x.label_spec() { + p = match s { + ast::LabelSpec::DefaultLabel(n) => build_keyword_tokens([ + (n.default_token(), "default"), + (n.label_token(), "label"), + ]), + ast::LabelSpec::NamedLabel(n) => { + let mut q = Doc::text("label"); + if let Some(l) = n.label() { + q = q + .append(Doc::space()) + .append(leading_comments(l.syntax())) + .append(build_name(l.syntax())); + } + q + } + }; } + if let Some(v) = x.element_table_properties() { + p = p + .append(Doc::space()) + .append(leading_comments(v.syntax())) + .append(build_element_properties(v)); + } + d = if i == 0 { + p + } else { + d.append(Doc::line_or_space()).append(p) + }; } - let body = build_comma_separated_exprs(values.exprs()).unwrap_or_else(Doc::nil); - doc.append(Doc::text("(")) - .append(wrap_body(body)) - .append(Doc::text(")")) - .group() + d } - ast::PartitionType::PartitionForValuesFrom(values) => { - let mut doc = build_partition_for_values_prefix(values.values_token()); - if let Some(from_token) = values.from_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&from_token)); - } else { - doc = doc.append(Doc::space()); - } - doc = doc.append(Doc::text("from")); - if let Some(from) = values.from() { - doc = doc - .append(Doc::space()) - .append(leading_comments(from.syntax())) - .append(build_create_table_partition_values( - from.l_paren_token(), - from.exprs(), - from.r_paren_token(), - )); - } - doc = doc.append(Doc::line_or_space()); - if let Some(to_token) = values.to_token() { - doc = doc.append(leading_comments_token(&to_token)); - } - doc = doc.append(Doc::text("to")); - if let Some(to) = values.to() { - doc = doc - .append(Doc::space()) - .append(leading_comments(to.syntax())) - .append(build_create_table_partition_values( - to.l_paren_token(), - to.exprs(), - to.r_paren_token(), - )); - } - doc.group() + } +} +fn build_element_properties<'a>(p: ast::ElementTableProperties) -> Doc<'a> { + match p { + ast::ElementTableProperties::AllProperties(n) => build_keyword_tokens([ + (n.properties_token(), "properties"), + (n.all_token(), "all"), + (n.columns_token(), "columns"), + ]), + ast::ElementTableProperties::NoProperties(n) => { + build_keyword_tokens([(n.no_token(), "no"), (n.properties_token(), "properties")]) } - ast::PartitionType::PartitionForValuesWith(values) => { - let mut doc = build_partition_for_values_prefix(values.values_token()); - if let Some(with_token) = values.with_token() { - doc = doc + ast::ElementTableProperties::Properties(n) => { + let mut d = n + .properties_token() + .map(|token| leading_comments_token(&token).append(Doc::text("properties"))) + .unwrap_or_else(Doc::nil); + if let Some(x) = n.expr_as_property_name_list() { + d = d .append(Doc::space()) - .append(leading_comments_token(&with_token)); - } else { - doc = doc.append(Doc::space()); - } - doc = doc.append(Doc::text("with")); - if let Some(l_paren) = values.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); - } - } - let mut parts = Vec::new(); - if let Some(modulus) = values.modulus() { - parts.push( - leading_comments(modulus.syntax()) - .append(build_keyword_node(modulus.syntax())) - .append(trailing_comments(modulus.syntax())), - ); - } - if let Some(remainder) = values.remainder() { - parts.push( - leading_comments(remainder.syntax()) - .append(build_keyword_node(remainder.syntax())) - .append(trailing_comments(remainder.syntax())), - ); + .append(leading_comments(x.syntax())) + .append(build_expr_property_list(x)); } - let body = Doc::list( - Itertools::intersperse( - parts.into_iter(), - Doc::text(",").append(Doc::line_or_space()), - ) - .collect(), - ); - doc.append(Doc::text("(")) - .append(wrap_body(body)) - .append(Doc::text(")")) - .group() + d } } } - -fn build_partition_for_values_prefix<'a>(values_token: Option) -> Doc<'a> { - let mut doc = Doc::text("for"); - if let Some(values_token) = values_token { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&values_token)); - } else { - doc = doc.append(Doc::space()); +fn build_expr_property_list<'a>(n: ast::ExprAsPropertyNameList) -> Doc<'a> { + let items = n.expr_as_property_names().map(|x| { + let mut d = x.expr().map(build_expr).unwrap_or_else(Doc::nil); + if let Some(t) = x.as_token() { + d = d + .append(Doc::space()) + .append(leading_comments_token(&t)) + .append(Doc::text("as")); + } + if let Some(p) = x.property_name() { + d = d + .append(Doc::space()) + .append(leading_comments(p.syntax())) + .append(build_name(p.syntax())); + } + (leading_comments(x.syntax()).append(d), x.syntax().clone()) + }); + let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); + if let Some(r) = n.r_paren_token() { + body = body.append(comments_before(r)); } - doc.append(Doc::text("values")) -} - -fn build_create_table_partition_values<'a>( - l_paren: Option, - exprs: impl Iterator, - _r_paren: Option, -) -> Doc<'a> { - let doc = l_paren + n.l_paren_token() .map(comments_before) .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let body = build_comma_separated_exprs(exprs).unwrap_or_else(Doc::nil); - doc.append(wrap_body(body)).append(Doc::text(")")).group() -} - -fn build_path<'a>(path: &ast::Path) -> Doc<'a> { - build_path_parts(path.qualifier(), path.dot_token(), path.segment()) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() } -fn build_path_ref<'a>(path: &ast::PathRef) -> Doc<'a> { - build_path_parts(path.qualifier(), path.dot_token(), path.segment()) +fn build_owner_to_role<'a>(owner: &ast::OwnerTo) -> Doc<'a> { + let mut doc = Doc::text("owner"); + if let Some(to) = owner.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + if let Some(role) = owner.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + doc } -fn build_path_parts<'a>( - qualifier: Option, - dot: Option, - segment: Option, +fn build_depends_on_extension<'a>( + no: bool, + depends: Option, + on: Option, + extension: Option, + name: Option, ) -> Doc<'a> { let mut doc = Doc::nil(); - if let Some(qualifier) = qualifier { - doc = doc - .append(build_path_ref(&qualifier)) - .append(trailing_comments(qualifier.syntax())); + let mut has_keyword = false; + if no { + doc = doc.append(Doc::text("no")); + has_keyword = true; } - if dot.is_some() { - doc = doc.append(Doc::text(".")); + for (token, keyword) in [(depends, "depends"), (on, "on"), (extension, "extension")] { + if let Some(token) = token { + if has_keyword { + doc = doc.append(Doc::space()); + } + doc = doc + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + has_keyword = true; + } } - if let Some(segment) = segment { + if let Some(name) = name { doc = doc - .append(leading_comments(segment.syntax())) - .append(build_name(segment.syntax())); + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); } doc } -fn build_name<'a>(node: &SyntaxNode) -> Doc<'a> { - let mut tokens = node - .children_with_tokens() - .filter_map(|el| el.into_token()) - .filter(|token| token.kind() != SyntaxKind::WHITESPACE); - - let Some(ident) = tokens.next() else { - return Doc::nil(); - }; - - if is_unicode_escape(ident.text()) { - let mut doc = Doc::text(ident.text().to_string()); - for token in tokens { - let text = match token.kind() { - SyntaxKind::STRING | SyntaxKind::COMMENT => token.text().to_string(), - _ => token.text().to_ascii_lowercase(), - }; - doc = doc.append(Doc::space()).append(Doc::text(text)); - if is_line_comment(&token) { - doc = doc.append(Doc::hard_line()); - } - } - return doc; +fn build_alter_function_options<'a>(list: &ast::FuncOptionList) -> Doc<'a> { + let mut doc = Doc::nil(); + for (index, option) in list.options().enumerate() { + let option_doc = leading_comments(option.syntax()).append(build_function_option(option)); + doc = if index == 0 { + option_doc + } else { + doc.append(Doc::line_or_space().append(option_doc).nest(2)) + }; } - - Doc::text(quote_ident(&normalize_name_node(node))) + doc.group() } -fn is_unicode_escape(text: &str) -> bool { - text.strip_prefix(['u', 'U']) - .is_some_and(|text| text.starts_with("&\"")) +fn build_alter_group<'a>(stmt: &ast::AlterGroup) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.group_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("group")); + } + if let Some(role) = stmt.role_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)); + } + if let Some(action) = stmt.action() { + let action_syntax = action.syntax().clone(); + let action_doc = match action { + ast::AlterGroupAction::AddUsers(node) => { + build_group_users("add", node.user_token(), node.role_refs()) + } + ast::AlterGroupAction::DropUsers(node) => { + build_group_users("drop", node.user_token(), node.role_refs()) + } + ast::AlterGroupAction::RoleRenameTo(node) => { + let target = node + .role() + .map(|role| leading_comments(role.syntax()).append(build_name(role.syntax()))); + build_rename_to(node.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&action_syntax)) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_table_arg<'a>(arg: ast::TableArg) -> Doc<'a> { - let doc = leading_comments(arg.syntax()); - let doc = doc.append(match &arg { - ast::TableArg::Column(column) => build_column(column), - ast::TableArg::LikeClause(like_clause) => build_like_clause(like_clause), - ast::TableArg::TableConstraint(table_constraint) => { - build_table_constraint(table_constraint.clone()) - } - }); - doc.append(trailing_comments(arg.syntax())) +fn build_group_users<'a>( + keyword: &'static str, + user: Option, + roles: impl Iterator, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(user) = user { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&user)) + .append(Doc::text("user")); + } + let roles = build_comma_separated_docs(roles.map(|role| { + let syntax = role.syntax().clone(); + ( + leading_comments(&syntax).append(build_role_ref(&role)), + syntax, + ) + })) + .unwrap_or_else(Doc::nil); + doc.append(Doc::line_or_space().append(roles).nest(2)) + .group() } -fn build_column<'a>(column: &ast::Column) -> Doc<'a> { - let mut doc = column - .name() - .map(|name| build_name(name.syntax())) - .unwrap_or_else(Doc::nil); - if let Some(ty) = column.ty() { +fn build_all_in_tablespace<'a>(all: ast::AllInTablespace) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (all.all_token(), "all"), + (all.in_token(), "in"), + (all.tablespace_token(), "tablespace"), + ]); + if let Some(tablespace) = all.tablespace_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(ty.syntax())) - .append(build_type(ty)); - } - if let Some(storage) = column.storage() { - let mut clause = Doc::text("storage"); - if let Some(mode) = storage.storage_mode() { - clause = clause - .append(Doc::space()) - .append(leading_comments(mode.syntax())) - .append(build_keyword_node(mode.syntax())); - } - doc = append_column_clause(doc, storage.syntax(), clause); + .append(leading_comments(tablespace.syntax())) + .append(build_name(tablespace.syntax())); } - if let Some(compression) = column.compression_method() { - let mut clause = Doc::text("compression"); - if let Some(method) = compression.compression_method_name() { - clause = clause + if let Some(owned) = all.owned_by_roles() { + let mut owned_doc = + build_keyword_tokens([(owned.owned_token(), "owned"), (owned.by_token(), "by")]); + if let Some(roles) = owned.role_ref_list() { + owned_doc = owned_doc .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(build_keyword_node(method.syntax())); + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)); } - doc = append_column_clause(doc, compression.syntax(), clause); - } - if let Some(options) = column.with_options() { - doc = append_column_clause(doc, options.syntax(), build_keyword_node(options.syntax())); - } - if let Some(options) = column.alter_option_list() { - let syntax = options.syntax().clone(); - doc = append_column_clause(doc, &syntax, build_alter_option_list(options)); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(owned.syntax())) + .append(owned_doc) + .nest(2), + ); } - if let Some(collate) = column.collate() { - let syntax = collate.syntax().clone(); - doc = append_column_clause(doc, &syntax, build_collate_expr(collate)); + if let Some(set) = all.set_tablespace() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(set.syntax())) + .append(build_set_tablespace(set)) + .nest(2), + ); } - for constraint in column.constraints() { - let syntax = constraint.syntax().clone(); - doc = append_column_clause(doc, &syntax, build_column_constraint(constraint)); + if let Some(nowait) = all.nowait() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(nowait.syntax())) + .append(build_keyword_tokens([(nowait.nowait_token(), "nowait")])) + .nest(2), + ); } doc.group() } -fn append_column_clause<'a>(doc: Doc<'a>, syntax: &SyntaxNode, clause: Doc<'a>) -> Doc<'a> { - doc.append( - Doc::line_or_space() - .append(leading_comments(syntax)) - .append(clause) - .nest(2), - ) -} - -fn build_alter_option_list<'a>(list: ast::AlterOptionList) -> Doc<'a> { - let mut doc = Doc::text("options"); - if let Some(l_paren) = list.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); - } - } else { - doc = doc.append(Doc::space()); - } - let items = list.alter_options().map(|option| { - let item = leading_comments(option.syntax()).append(build_alter_option(&option)); - (item, option.syntax().clone()) - }); - let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - if let Some(r_paren) = list.r_paren_token() { - body = body.append(comments_before(r_paren)); +fn build_alter_index<'a>(stmt: &ast::AlterIndex) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.index_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("index")); } - doc.append(Doc::text("(")) - .append(Doc::hard_line().append(body).nest(2)) - .append(Doc::hard_line()) - .append(Doc::text(")")) -} - -fn build_alter_option<'a>(option: &ast::AlterOption) -> Doc<'a> { - match option { - ast::AlterOption::AddForeignOption(option) => { - let mut doc = option - .add_token() - .map(|token| leading_comments_token(&token).append(Doc::text("add"))) - .unwrap_or_else(Doc::nil); - if let Some(name) = option.foreign_option_name() { - if option.add_token().is_some() { - doc = doc.append(Doc::space()); - } - doc = doc - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } - if let Some(value) = option.literal() { - doc = doc - .append(Doc::space()) - .append(leading_comments(value.syntax())) - .append(build_literal(value)); - } - doc + if let Some(all) = stmt.all_in_tablespace() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(all.syntax())) + .append(build_all_in_tablespace(all)) + .nest(2), + ); + } else { + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - ast::AlterOption::SetForeignOption(option) => { - let mut doc = Doc::text("set"); - if let Some(name) = option.foreign_option_name() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } - if let Some(value) = option.literal() { - doc = doc - .append(Doc::space()) - .append(leading_comments(value.syntax())) - .append(build_literal(value)); + if let Some(index) = stmt.index_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(index.syntax())); + if let Some(path) = index.path_ref() { + doc = doc.append(build_path_ref(&path)); } - doc } - ast::AlterOption::DropForeignOption(option) => { - let mut doc = Doc::text("drop"); - if let Some(name) = option.foreign_option_name() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } - doc + if let Some(action) = stmt.action() { + let syntax = action.syntax().clone(); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(&syntax)) + .append(build_alter_index_action(action)) + .nest(2), + ); } } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_column_constraint<'a>(constraint: ast::ColumnConstraint) -> Doc<'a> { - match constraint { - ast::ColumnConstraint::CheckConstraint(constraint) => build_check_constraint(constraint), - ast::ColumnConstraint::DefaultConstraint(constraint) => { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(default) = constraint.default_token() { - doc = doc - .append(leading_comments_token(&default)) - .append(Doc::text("default")); - } - if let Some(expr) = constraint.expr() { +fn build_alter_index_action<'a>(action: ast::AlterIndexAction) -> Doc<'a> { + match action { + ast::AlterIndexAction::AlterSetStatistics(action) => { + let mut doc = build_keyword_tokens([ + (action.alter_token(), "alter"), + (action.column_token(), "column"), + ]); + if let Some(column) = action.column_name_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); - } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() - } - ast::ColumnConstraint::ExcludeConstraint(constraint) => { - build_exclude_constraint(constraint) - } - ast::ColumnConstraint::GeneratedConstraint(constraint) => { - build_generated_constraint(constraint) - } - ast::ColumnConstraint::NotNullConstraint(constraint) => { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(not) = constraint.not_token() { - doc = doc - .append(leading_comments_token(¬)) - .append(Doc::text("not")); - } - if let Some(null) = constraint.null_token() { + .append(leading_comments(column.syntax())) + .append(build_name(column.syntax())); + } else if let Some(number) = action.literal() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&null)) - .append(Doc::text("null")); + .append(leading_comments(number.syntax())) + .append(build_literal(number)); } - if let Some(column) = constraint.column_name_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(column.syntax())) - .append(build_name(column.syntax())); + if let Some(options) = action.set_options() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(options.syntax())) + .append(build_set_options(&options)) + .nest(2), + ); + } else if let Some(statistics) = action.set_statistics() { + let mut statistics_doc = build_keyword_tokens([ + (statistics.set_token(), "set"), + (statistics.statistics_token(), "statistics"), + ]); + if let Some(value) = statistics.expr() { + statistics_doc = statistics_doc + .append(Doc::space()) + .append(leading_comments(value.syntax())) + .append(build_expr(value)); + } else if let Some(default) = statistics.default_token() { + statistics_doc = statistics_doc + .append(Doc::space()) + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(statistics.syntax())) + .append(statistics_doc) + .nest(2), + ); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() + doc.group() } - ast::ColumnConstraint::NullConstraint(constraint) => { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(null) = constraint.null_token() { + ast::AlterIndexAction::AttachIndexPartition(action) => { + let mut doc = build_keyword_tokens([ + (action.attach_token(), "attach"), + (action.partition_token(), "partition"), + ]); + if let Some(index) = action.index_ref() { doc = doc - .append(leading_comments_token(&null)) - .append(Doc::text("null")); + .append(Doc::space()) + .append(leading_comments(index.syntax())); + if let Some(path) = index.path_ref() { + doc = doc.append(build_path_ref(&path)); + } } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() + doc } - ast::ColumnConstraint::PrimaryKeyConstraint(constraint) => { - build_primary_key_constraint(constraint) + ast::AlterIndexAction::DependsOnExtension(action) => build_depends_on_extension( + false, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ), + ast::AlterIndexAction::NoDependsOnExtension(action) => build_depends_on_extension( + true, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ), + ast::AlterIndexAction::IndexRenameTo(action) => { + let target = action.index().map(|index| { + leading_comments(index.syntax()).append( + index + .path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) } - ast::ColumnConstraint::ReferencesConstraint(constraint) => { - build_references_constraint(constraint) + ast::AlterIndexAction::ResetOptions(action) => { + let mut doc = Doc::text("reset"); + if let Some(attributes) = action.attribute_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + doc } - ast::ColumnConstraint::UniqueConstraint(constraint) => build_unique_constraint(constraint), + ast::AlterIndexAction::SetOptions(action) => build_set_options(&action), + ast::AlterIndexAction::SetTablespace(action) => build_set_tablespace(action), } } -fn build_references_constraint<'a>(constraint: ast::ReferencesConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(references) = constraint.references_token() { - doc = doc - .append(leading_comments_token(&references)) - .append(Doc::text("references")); +fn build_alter_language<'a>(stmt: &ast::AlterLanguage) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.procedural_token(), "procedural"), + (stmt.language_token(), "language"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } } - if let Some(table) = constraint.table() { + if let Some(language) = stmt.language_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(table.syntax())); - if let Some(path) = table.path_ref() { - doc = doc.append(build_path_ref(&path)); - } + .append(leading_comments(language.syntax())) + .append(build_name(language.syntax())); } - if let Some(column) = constraint.column() { - if let Some(l_paren) = constraint.l_paren_token() { - doc = doc.append(comments_before(l_paren)); + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterLanguageAction::LanguageRenameTo(action) => { + let target = action.language().map(|language| { + leading_comments(language.syntax()).append(build_name(language.syntax())) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterLanguageAction::OwnerTo(action) => build_owner_to_role(action), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_large_object<'a>(stmt: &ast::AlterLargeObject) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.large_token(), "large"), + (stmt.object_token(), "object"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } + } + if let Some(id) = stmt.literal() { doc = doc - .append(Doc::text("(")) - .append(leading_comments(column.syntax())) - .append(build_name(column.syntax())); - if let Some(r_paren) = constraint.r_paren_token() { - doc = doc.append(comments_before(r_paren)); + .append(Doc::space()) + .append(leading_comments(id.syntax())) + .append(build_literal(id)); + } + let mut owner: Option> = None; + for (token, keyword) in [(stmt.owner_token(), "owner"), (stmt.to_token(), "to")] { + if let Some(token) = token { + let keyword = leading_comments_token(&token).append(Doc::text(keyword)); + owner = Some(match owner { + Some(owner) => owner.append(Doc::space()).append(keyword), + None => keyword, + }); } - doc = doc.append(Doc::text(")")); } - if let Some(match_type) = constraint.match_type() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(match_type.syntax())) - .append(build_keyword_node(match_type.syntax())); + if let Some(role) = stmt.role_ref() { + let role = leading_comments(role.syntax()).append(build_role_ref(&role)); + owner = Some(match owner { + Some(owner) => owner.append(Doc::space()).append(role), + None => role, + }); } - if let Some(action) = constraint.on_delete_action() { + if let Some(owner) = owner { + doc = doc.append(Doc::line_or_space().append(owner).nest(2)); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_operator<'a>(stmt: &ast::AlterOperator) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.operator_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(action.syntax())) - .append(build_reference_action( - action.on_token(), - action.delete_token(), - "delete", - action.ref_action(), - )); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("operator")); } - if let Some(action) = constraint.on_update_action() { + if let Some(sig) = stmt.op_sig() { doc = doc - .append(Doc::line_or_space()) + .append(Doc::space()) + .append(leading_comments(sig.syntax())) + .append(build_op_sig(sig)); + } + if let Some(action) = stmt.action() { + let is_set_options = matches!(&action, ast::AlterOperatorAction::SetOptions(_)); + let separator = if is_set_options { + Doc::space() + } else { + Doc::line_or_space() + }; + let action_doc = match &action { + ast::AlterOperatorAction::OwnerTo(action) => build_owner_to(action), + ast::AlterOperatorAction::SetOptions(action) => build_set_options(action), + ast::AlterOperatorAction::SetSchema(action) => build_set_schema(action), + }; + let action_doc = separator .append(leading_comments(action.syntax())) - .append(build_reference_action( - action.on_token(), - action.update_token(), - "update", - action.ref_action(), - )); + .append(action_doc); + doc = doc.append(if is_set_options { + action_doc + } else { + action_doc.nest(2) + }); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_op_sig<'a>(sig: ast::OpSig) -> Doc<'a> { + let mut doc = sig + .op() + .map(|op| leading_comments(op.syntax()).append(build_ddl_operator(&op))) + .unwrap_or_else(Doc::nil); + if let Some(l_paren) = sig.l_paren_token() { + doc = doc.append(comments_before(l_paren)); + } + let has_none = sig.none_token().is_some(); + let lhs = if let Some(none) = sig.none_token() { + leading_comments_token(&none).append(Doc::text("none")) + } else if let Some(lhs) = sig.lhs() { + leading_comments(lhs.syntax()).append(build_type(lhs)) + } else { + Doc::nil() + }; + let mut body = lhs; + if let Some(comma) = sig.comma_token() { + body = body + .append(comments_before(comma)) + .append(Doc::text(",")) + .append(Doc::line_or_space()); + } + let rhs = if has_none { sig.lhs() } else { sig.rhs() }; + if let Some(rhs) = rhs { + body = body + .append(leading_comments(rhs.syntax())) + .append(build_type(rhs)); + } + if let Some(r_paren) = sig.r_paren_token() { + body = body.append(comments_before(r_paren)); + } + doc.append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) .group() } -fn build_generated_constraint<'a>(constraint: ast::GeneratedConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(generated) = constraint.generated_token() { +fn build_ddl_operator<'a>(op: &ast::Op) -> Doc<'a> { + let mut doc = op + .path_ref() + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil); + if let Some(custom) = op.custom_op() { + return doc + .append(leading_comments(custom.syntax())) + .append(build_custom_operator(custom)); + } + + let token = [ + (op.neq_token(), "!="), + (op.percent_token(), "%"), + (op.plus_token(), "+"), + (op.minus_token(), "-"), + (op.slash_token(), "/"), + (op.colon_token(), ":"), + (op.colon_eq_token(), ":="), + (op.l_angle_token(), "<"), + (op.lteq_token(), "<="), + (op.neqb_token(), "<>"), + (op.eq_token(), "="), + (op.fat_arrow_token(), "=>"), + (op.r_angle_token(), ">"), + (op.gteq_token(), ">="), + (op.caret_token(), "^"), + ] + .into_iter() + .find_map(|(token, text)| token.map(|token| (token, text))); + if let Some((token, text)) = token { doc = doc - .append(leading_comments_token(&generated)) - .append(Doc::text("generated")); + .append(leading_comments_token(&token)) + .append(Doc::text(text)); } - if let Some(generated_as) = constraint.generated_as() { + doc +} + +fn build_alter_operator_class<'a>(stmt: &ast::AlterOperatorClass) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.operator_token(), "operator"), + (stmt.class_token(), "class"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } + } + if let Some(class) = stmt.op_class_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(generated_as.syntax())) - .append(match generated_as { - ast::GeneratedAs::GeneratedIdentity(identity) => { - let mut body = identity - .generated_when() - .map(build_generated_when) - .unwrap_or_else(Doc::nil); - if let Some(as_token) = identity.as_token() { - body = body - .append(Doc::space()) - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); - } - if let Some(identity_token) = identity.identity_token() { - body = body - .append(Doc::space()) - .append(leading_comments_token(&identity_token)) - .append(Doc::text("identity")); - } - if let Some(options) = identity.sequence_option_list() { - body = body - .append(Doc::space()) - .append(leading_comments(options.syntax())) - .append(build_sequence_option_list(options)); - } - body - } - ast::GeneratedAs::GeneratedStored(stored) => { - let mut body = stored - .generated_when() - .map(build_generated_when) - .unwrap_or_else(Doc::nil); - if let Some(as_token) = stored.as_token() { - body = body - .append(Doc::space()) - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); - } - if let Some(l_paren) = stored.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - body = body.append(Doc::space()); - } else { - body = body.append(comments_before(l_paren)); - } - } - let mut expr = stored - .expr() - .map(|expr| leading_comments(expr.syntax()).append(build_expr(expr))) - .unwrap_or_else(Doc::nil); - if let Some(r_paren) = stored.r_paren_token() { - expr = expr.append(comments_before(r_paren)); - } - body = body - .append(Doc::text("(")) - .append(wrap_body(expr)) - .append(Doc::text(")")); - if let Some(kind) = stored.generated_kind() { - body = body - .append(Doc::space()) - .append(leading_comments(kind.syntax())) - .append(build_keyword_node(kind.syntax())); - } - body.group() - } - }); + .append(leading_comments(class.syntax())); + if let Some(path) = class.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(using) = stmt.using_method() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) + .nest(2), + ); + } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterOperatorClassAction::OpClassRenameTo(action) => { + let target = action.op_class_name().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterOperatorClassAction::OwnerTo(action) => build_owner_to(action), + ast::AlterOperatorClassAction::SetSchema(action) => build_set_schema(action), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_operator_family<'a>(stmt: &ast::AlterOperatorFamily) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.operator_token(), "operator"), + (stmt.family_token(), "family"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); + } } - doc = doc.group(); - for option in constraint.constraint_options() { + if let Some(family) = stmt.op_family_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(family.syntax())); + if let Some(path) = family.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(using) = stmt.using_method() { doc = doc.append( Doc::line_or_space() - .append(leading_comments(option.syntax())) - .append(build_keyword_node(option.syntax())) + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) .nest(2), ); } - doc.group() -} - -fn build_generated_when<'a>(when: ast::GeneratedWhen) -> Doc<'a> { - match when { - ast::GeneratedWhen::GeneratedAlways(always) => build_keyword_node(always.syntax()), - ast::GeneratedWhen::GeneratedByDefault(by_default) => { - build_keyword_node(by_default.syntax()) - } + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterOperatorFamilyAction::AddOpClassOptions(action) => { + build_add_op_class_options(action) + } + ast::AlterOperatorFamilyAction::DropOpClassOptions(action) => { + build_drop_op_class_options(action) + } + ast::AlterOperatorFamilyAction::OpFamilyRenameTo(action) => { + let target = action.op_family_name().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterOperatorFamilyAction::OwnerTo(action) => build_owner_to(action), + ast::AlterOperatorFamilyAction::SetSchema(action) => build_set_schema(action), + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_sequence_option_list<'a>(list: ast::SequenceOptionList) -> Doc<'a> { - let doc = list - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let options = list - .sequence_options() - .map(|option| leading_comments(option.syntax()).append(build_sequence_option(option))); - let mut body = Doc::list(Itertools::intersperse(options, Doc::line_or_space()).collect()); - if let Some(r_paren) = list.r_paren_token() { - body = body.append(comments_before(r_paren)); +fn build_add_op_class_options<'a>(action: &ast::AddOpClassOptions) -> Doc<'a> { + let mut doc = Doc::text("add"); + if let Some(list) = action.operator_class_option_list() { + let options = list.op_class_options().map(|option| { + let syntax = option.syntax().clone(); + ( + leading_comments(&syntax).append(build_op_class_option(option)), + syntax, + ) + }); + if let Some(options) = build_comma_separated_docs(options) { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(options) + .nest(2), + ); + } } - doc.append(Doc::hard_line().append(body).nest(2)) - .append(Doc::hard_line()) - .append(Doc::text(")")) + doc.group() } -fn build_sequence_option<'a>(option: ast::SequenceOption) -> Doc<'a> { +fn build_op_class_option<'a>(option: ast::OpClassOption) -> Doc<'a> { match option { - ast::SequenceOption::OptionAsType(option) => { - let mut doc = Doc::text("as"); - if let Some(ty) = option.ty() { + ast::OpClassOption::OpClassOptionFunction(option) => { + let mut doc = Doc::text("function"); + if let Some(number) = option.literal() { doc = doc .append(Doc::space()) - .append(leading_comments(ty.syntax())) - .append(build_type(ty)); + .append(leading_comments(number.syntax())) + .append(build_literal(number)); } - doc - } - ast::SequenceOption::OptionCache(option) => { - append_optional_literal(Doc::text("cache"), option.literal()) - } - ast::SequenceOption::OptionIncrement(option) => { - let mut doc = option - .increment_token() - .map(|token| leading_comments_token(&token).append(Doc::text("increment"))) - .unwrap_or_else(Doc::nil); - if let Some(by) = option.by_token() { + if let Some(params) = option.param_list() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&by)) - .append(Doc::text("by")); + .append(leading_comments(params.syntax())) + .append(build_function_param_list(params)); } - append_optional_literal(doc, option.literal()) - } - ast::SequenceOption::OptionMaxValue(option) => { - append_optional_literal(Doc::text("maxvalue"), option.literal()) - } - ast::SequenceOption::OptionMinValue(option) => { - append_optional_literal(Doc::text("minvalue"), option.literal()) - } - ast::SequenceOption::OptionRestart(option) => { - let mut doc = option - .restart_token() - .map(|token| leading_comments_token(&token).append(Doc::text("restart"))) - .unwrap_or_else(Doc::nil); - if let Some(with) = option.with_token() { + if let Some(function) = option.function_sig() { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&with)) - .append(Doc::text("with")); + .append(Doc::line_or_space()) + .append(leading_comments(function.syntax())) + .append(build_function_sig(function)); } - append_optional_literal(doc, option.literal()) + doc.group() } - ast::SequenceOption::OptionStart(option) => { - let mut doc = option - .start_token() - .map(|token| leading_comments_token(&token).append(Doc::text("start"))) - .unwrap_or_else(Doc::nil); - if let Some(with) = option.with_token() { + ast::OpClassOption::OpClassOptionOperator(option) => { + let mut doc = Doc::text("operator"); + if let Some(number) = option.literal() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&with)) - .append(Doc::text("with")); + .append(leading_comments(number.syntax())) + .append(build_literal(number)); } - append_optional_literal(doc, option.literal()) - } - ast::SequenceOption::OptionOwnedBy(option) => { - let mut doc = option - .owned_token() - .map(|token| leading_comments_token(&token).append(Doc::text("owned"))) - .unwrap_or_else(Doc::nil); - if let Some(by) = option.by_token() { + if let Some(op) = option.op() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&by)) - .append(Doc::text("by")); + .append(leading_comments(op.syntax())) + .append(build_ddl_operator(&op)); } - if let Some(target) = option.owned_by_target() { - doc = doc - .append(Doc::space()) - .append(leading_comments(target.syntax())) - .append(match target { - ast::OwnedByTarget::OwnedByNone(_) => Doc::text("none"), - ast::OwnedByTarget::QualifiedColumnNameRef(name) => name - .path_ref() - .map(|path| build_path_ref(&path)) - .unwrap_or_else(Doc::nil), - }); + if let Some(l_paren) = option.l_paren_token() { + if comment_tokens_before(l_paren).is_empty() { + doc = doc.append(Doc::space()); + } + doc = doc.append(build_op_class_type_pair(&option)); } - doc - } - ast::SequenceOption::OptionSequenceName(option) => { - let mut doc = option - .sequence_token() - .map(|token| leading_comments_token(&token).append(Doc::text("sequence"))) - .unwrap_or_else(Doc::nil); - if let Some(name) = option.name_token() { + if let Some(purpose) = option.op_class_purpose() { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&name)) - .append(Doc::text("name")); + .append(Doc::line_or_space()) + .append(leading_comments(purpose.syntax())) + .append(build_op_class_purpose(purpose)); } - if let Some(sequence) = option.sequence() { + doc.group() + } + ast::OpClassOption::OpClassOptionStorage(option) => { + let mut doc = Doc::text("storage"); + if let Some(ty) = option.ty() { doc = doc .append(Doc::space()) - .append(leading_comments(sequence.syntax())); - if let Some(path) = sequence.path() { - doc = doc.append(build_path(&path)); - } + .append(leading_comments(ty.syntax())) + .append(build_type(ty)); } doc } - ast::SequenceOption::OptionCycle(option) => build_keyword_node(option.syntax()), - ast::SequenceOption::OptionLogged(option) => build_keyword_node(option.syntax()), - ast::SequenceOption::OptionNoCycle(option) => build_keyword_node(option.syntax()), - ast::SequenceOption::OptionNoMaxValue(option) => build_keyword_node(option.syntax()), - ast::SequenceOption::OptionNoMinValue(option) => build_keyword_node(option.syntax()), - ast::SequenceOption::OptionUnlogged(option) => build_keyword_node(option.syntax()), - } -} - -fn append_optional_literal<'a>(doc: Doc<'a>, literal: Option) -> Doc<'a> { - literal.map_or(doc.clone(), |literal| { - doc.append(Doc::space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)) - }) -} - -fn build_table_constraint<'a>(constraint: ast::TableConstraint) -> Doc<'a> { - match constraint { - ast::TableConstraint::CheckConstraint(constraint) => build_check_constraint(constraint), - ast::TableConstraint::ExcludeConstraint(constraint) => build_exclude_constraint(constraint), - ast::TableConstraint::ForeignKeyConstraint(constraint) => { - build_foreign_key_constraint(constraint) - } - ast::TableConstraint::PrimaryKeyConstraint(constraint) => { - build_primary_key_constraint(constraint) - } - ast::TableConstraint::UniqueConstraint(constraint) => build_unique_constraint(constraint), - } -} - -fn build_constraint_name_clause<'a>(clause: Option) -> Doc<'a> { - let Some(clause) = clause else { - return Doc::nil(); - }; - let mut doc = Doc::text("constraint"); - if let Some(name) = clause.constraint_name() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); } - doc.append(Doc::space()) } -fn build_check_constraint<'a>(constraint: ast::CheckConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(check) = constraint.check_token() { - doc = doc - .append(leading_comments_token(&check)) - .append(Doc::text("check")); - } - if let Some(l_paren) = constraint.l_paren_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&l_paren)); +fn build_op_class_type_pair<'a>(option: &ast::OpClassOptionOperator) -> Doc<'a> { + let mut body = option + .lhs() + .map(|ty| leading_comments(ty.syntax()).append(build_type(ty))) + .unwrap_or_else(Doc::nil); + if let Some(comma) = option.comma_token() { + body = body + .append(comments_before(comma)) + .append(Doc::text(",")) + .append(Doc::line_or_space()); } - doc = doc.append(Doc::text("(")); - - let mut body = Doc::nil(); - if let Some(expr) = constraint.expr() { + if let Some(rhs) = option.rhs() { body = body - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); + .append(leading_comments(rhs.syntax())) + .append(build_type(rhs)); } - if let Some(r_paren) = constraint.r_paren_token() { + if let Some(r_paren) = option.r_paren_token() { body = body.append(comments_before(r_paren)); } - doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); + option + .l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() +} - let mut options = Doc::nil(); - for option in constraint.constraint_options() { - options = options - .append(Doc::line_or_space()) - .append(leading_comments(option.syntax())) - .append(build_keyword_node(option.syntax())); +fn build_op_class_purpose<'a>(purpose: ast::OpClassPurpose) -> Doc<'a> { + match purpose { + ast::OpClassPurpose::OpClassForSearch(purpose) => build_keyword_tokens([ + (purpose.for_token(), "for"), + (purpose.search_token(), "search"), + ]), + ast::OpClassPurpose::OpClassForOrderBy(purpose) => { + let mut doc = build_keyword_tokens([ + (purpose.for_token(), "for"), + (purpose.order_token(), "order"), + (purpose.by_token(), "by"), + ]); + if let Some(family) = purpose.op_family_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(family.syntax())); + if let Some(path) = family.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc + } + } +} + +fn build_drop_op_class_options<'a>(action: &ast::DropOpClassOptions) -> Doc<'a> { + let mut doc = Doc::text("drop"); + if let Some(list) = action.drop_op_class_option_list() { + let options = list.drop_op_class_options().map(|option| { + let syntax = option.syntax().clone(); + let option_doc = match option { + ast::DropOpClassOption::DropOpClassOptionFunction(option) => { + build_drop_op_class_option("function", option.literal(), option.param_list()) + } + ast::DropOpClassOption::DropOpClassOptionOperator(option) => { + build_drop_op_class_option("operator", option.literal(), option.param_list()) + } + }; + (leading_comments(&syntax).append(option_doc), syntax) + }); + if let Some(options) = build_comma_separated_docs(options) { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(options) + .nest(2), + ); + } } - doc.append(options.nest(2)).group() + doc.group() } -fn build_primary_key_constraint<'a>(constraint: ast::PrimaryKeyConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(primary) = constraint.primary_token() { - doc = doc - .append(leading_comments_token(&primary)) - .append(Doc::text("primary")); - } - if let Some(key) = constraint.key_token() { +fn build_drop_op_class_option<'a>( + keyword: &'static str, + number: Option, + params: Option, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(number) = number { doc = doc .append(Doc::space()) - .append(leading_comments_token(&key)) - .append(Doc::text("key")); + .append(leading_comments(number.syntax())) + .append(build_literal(number)); } - if let Some(using_index) = constraint.using_index() { + if let Some(params) = params { doc = doc .append(Doc::space()) - .append(leading_comments(using_index.syntax())) - .append(build_using_index_name(using_index)); - } else if let Some(parameters) = constraint.index_parameters() { - doc = doc - .append(leading_comments(parameters.syntax())) - .append(build_index_parameters(parameters)); + .append(leading_comments(params.syntax())) + .append(build_function_param_list(params)); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() + doc } -fn build_unique_constraint<'a>(constraint: ast::UniqueConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(unique) = constraint.unique_token() { - doc = doc - .append(leading_comments_token(&unique)) - .append(Doc::text("unique")); - } - if let Some(using_index) = constraint.using_index() { +fn build_alter_policy<'a>(stmt: &ast::AlterPolicy) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.policy_token() { doc = doc .append(Doc::space()) - .append(leading_comments(using_index.syntax())) - .append(build_using_index_name(using_index)); - } else if let Some(parameters) = constraint.index_parameters() { - doc = doc - .append(leading_comments(parameters.syntax())) - .append(build_index_parameters(parameters)); + .append(leading_comments_token(&token)) + .append(Doc::text("policy")); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() -} - -fn build_using_index_name<'a>(using_index: ast::UsingIndexName) -> Doc<'a> { - let mut doc = Doc::text("using"); - if let Some(index) = using_index.index_token() { + if let Some(policy) = stmt.policy_ref() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&index)) - .append(Doc::text("index")); + .append(leading_comments(policy.syntax())) + .append(build_name(policy.syntax())); } - if let Some(index) = using_index.index_ref() { - if let Some(path) = index.path_ref() { - doc = doc + if let Some(on) = stmt.on_table() { + let mut on_doc = Doc::text("on"); + if let Some(table) = on.table_name_ref() { + on_doc = on_doc .append(Doc::space()) - .append(leading_comments(index.syntax())) - .append(build_path_ref(&path)); + .append(leading_comments(table.syntax())); + if let Some(path) = table.path_ref() { + on_doc = on_doc.append(build_path_ref(&path)); + } } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); } - doc + if let Some(action) = stmt.action() { + let action_doc = match &action { + ast::AlterPolicyAction::AlterPolicyTo(action) => build_alter_policy_to(action), + ast::AlterPolicyAction::PolicyRenameTo(action) => { + let target = action.policy().map(|policy| { + leading_comments(policy.syntax()).append(build_name(policy.syntax())) + }); + build_rename_to(action.to_token(), target) + } + }; + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(action_doc) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_index_parameters<'a>(parameters: ast::IndexParameters) -> Doc<'a> { - let mut doc = Doc::nil(); - if let Some(nulls) = parameters.nulls_distinct_option() { - doc = doc - .append(Doc::space()) - .append(leading_comments(nulls.syntax())) - .append(build_keyword_node(nulls.syntax())); - } - if let Some(columns) = parameters.column_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_constraint_column_ref_list(columns)); +fn build_alter_policy_to<'a>(action: &ast::AlterPolicyTo) -> Doc<'a> { + let mut docs = Vec::new(); + if let Some(roles) = action.policy_roles() { + let mut doc = Doc::text("to"); + if let Some(list) = roles.role_ref_list() { + doc = doc + .append(Doc::space()) + .append(leading_comments(list.syntax())) + .append(build_role_ref_list(list)); + } + docs.push(leading_comments(roles.syntax()).append(doc)); } - if let Some(include) = parameters.constraint_include_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(include.syntax())) - .append(build_constraint_include_clause(include)); + if let Some(using) = action.using_expr_clause() { + docs.push( + leading_comments(using.syntax()).append(build_policy_expr_clause( + build_keyword_tokens([(using.using_token(), "using")]), + using.l_paren_token(), + using.expr(), + using.r_paren_token(), + )), + ); } - if let Some(with_params) = parameters.with_params() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(with_params.syntax())) - .append(build_with_params(with_params)); + if let Some(check) = action.with_check_expr_clause() { + docs.push( + leading_comments(check.syntax()).append(build_policy_expr_clause( + build_keyword_tokens([ + (check.with_token(), "with"), + (check.check_token(), "check"), + ]), + check.l_paren_token(), + check.expr(), + check.r_paren_token(), + )), + ); } - if let Some(tablespace) = parameters.constraint_index_tablespace() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(tablespace.syntax())) - .append(build_constraint_index_tablespace(tablespace)); + let mut docs = docs.into_iter(); + let mut doc = docs.next().unwrap_or_else(Doc::nil); + for clause in docs { + doc = doc.append(Doc::line_or_space().append(clause)); } - doc -} - -fn build_constraint_column_ref_list<'a>(list: ast::ConstraintColumnRefList) -> Doc<'a> { - let suffix = list.without_overlaps().map(|overlaps| { - Doc::space() - .append(leading_comments(overlaps.syntax())) - .append(build_keyword_node(overlaps.syntax())) - }); - build_column_names( - list.l_paren_token(), - list.column_name_refs(), - suffix, - list.r_paren_token(), - ) -} - -fn build_column_ref_list<'a>(list: ast::ColumnRefList) -> Doc<'a> { - build_column_names( - list.l_paren_token(), - list.column_name_refs(), - None, - list.r_paren_token(), - ) + doc.group() } -fn build_column_names<'a>( +fn build_policy_expr_clause<'a>( + prefix: Doc<'a>, l_paren: Option, - names: impl Iterator, - suffix: Option>, + expr: Option, r_paren: Option, ) -> Doc<'a> { - let doc = l_paren - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let items = names.map(|name| { - ( - leading_comments(name.syntax()).append(build_name(name.syntax())), - name.syntax().clone(), - ) - }); - let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - if let Some(suffix) = suffix { - body = body.append(suffix); - } + let mut body = expr + .map(|expr| leading_comments(expr.syntax()).append(build_expr(expr))) + .unwrap_or_else(Doc::nil); if let Some(r_paren) = r_paren { body = body.append(comments_before(r_paren)); } - doc.append(wrap_body(body)).append(Doc::text(")")).group() -} - -fn build_constraint_include_clause<'a>(include: ast::ConstraintIncludeClause) -> Doc<'a> { - let mut doc = Doc::text("include"); - if let Some(columns) = include.column_ref_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); - } - doc -} - -fn build_with_params<'a>(with_params: ast::WithParams) -> Doc<'a> { - let mut doc = Doc::text("with"); - if let Some(attributes) = with_params.attribute_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(attributes.syntax())) - .append(build_attribute_list(attributes)); - } - doc + let before_paren = l_paren.map(space_before_l_paren).unwrap_or_else(Doc::space); + prefix + .append(before_paren) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append(Doc::text(")")) + .group() } -fn build_attribute_list<'a>(list: ast::AttributeList) -> Doc<'a> { - let doc = list - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let items = list.attribute_options().map(|option| { - let mut item = option - .namespace() - .map(|namespace| build_name(namespace.syntax())) - .unwrap_or_else(Doc::nil); - if let Some(dot) = option.dot_token() { - item = item.append(comments_before(dot)).append(Doc::text(".")); - } - if let Some(name) = option.name() { - item = item - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } - if let Some(eq) = option.eq_token() { - item = item +fn build_alter_materialized_view<'a>(stmt: &ast::AlterMaterializedView) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.materialized_token(), "materialized"), + (stmt.view_token(), "view"), + ] { + if let Some(token) = token { + doc = doc .append(Doc::space()) - .append(leading_comments_token(&eq)) - .append(Doc::text("=")); + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } - if let Some(value) = option.attribute_value() { - item = item + } + if let Some(all) = stmt.all_in_tablespace() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(all.syntax())) + .append(build_all_in_tablespace(all)) + .nest(2), + ); + } else { + if let Some(if_exists) = stmt.if_exists() { + doc = doc .append(Doc::space()) - .append(leading_comments(value.syntax())) - .append(build_attribute_value(value)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - ( - leading_comments(option.syntax()).append(item), - option.syntax().clone(), - ) - }); - let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - if let Some(r_paren) = list.r_paren_token() { - body = body.append(comments_before(r_paren)); - } - doc.append(wrap_body(body)).append(Doc::text(")")).group() -} - -fn build_attribute_value<'a>(value: ast::AttributeValue) -> Doc<'a> { - if let Some(literal) = value.literal() { - build_literal(literal) - } else if let Some(ty) = value.ty() { - build_type(ty) - } else if value.none_token().is_some() { - Doc::text("none") - } else if let Some(op) = value.op() { - if value.operator_token().is_some() { - let mut doc = Doc::text("operator"); - if let Some(l_paren) = value.l_paren_token() { - doc = doc.append(comments_before(l_paren)); - } - doc = doc.append(Doc::text("(")).append(build_operator(&op)); - if let Some(r_paren) = value.r_paren_token() { - doc = doc.append(comments_before(r_paren)); + if let Some(view) = stmt.view_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(view.syntax())); + if let Some(path) = view.path_ref() { + doc = doc.append(build_path_ref(&path)); } - doc.append(Doc::text(")")) - } else { - build_operator(&op) } - } else { - Doc::nil() + let actions = stmt.action().map(|action| { + let syntax = action.syntax().clone(); + ( + leading_comments(&syntax).append(build_alter_materialized_view_action(action)), + syntax, + ) + }); + if let Some(actions) = build_comma_separated_docs(actions) { + doc = doc.append(Doc::line_or_space().append(actions).nest(2)); + } } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_constraint_index_tablespace<'a>(tablespace: ast::ConstraintIndexTablespace) -> Doc<'a> { - let mut doc = Doc::text("using"); - if let Some(index) = tablespace.index_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&index)) - .append(Doc::text("index")); +fn build_alter_materialized_view_action<'a>(action: ast::AlterMaterializedViewAction) -> Doc<'a> { + match action { + ast::AlterMaterializedViewAction::DependsOnExtension(action) => build_depends_on_extension( + false, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ), + ast::AlterMaterializedViewAction::NoDependsOnExtension(action) => { + build_depends_on_extension( + true, + action.depends_token(), + action.on_token(), + action.extension_token(), + action.extension_ref(), + ) + } + ast::AlterMaterializedViewAction::RenameColumn(action) => { + let mut doc = build_keyword_tokens([ + (action.rename_token(), "rename"), + (action.column_token(), "column"), + ]); + doc = append_commented_name(doc, action.column_name_ref()); + if let Some(to) = action.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + append_commented_name(doc, action.column_name()) + } + ast::AlterMaterializedViewAction::SetSchema(action) => build_set_schema(&action), + ast::AlterMaterializedViewAction::ViewRenameTo(action) => { + let target = action.view().map(|view| { + leading_comments(view.syntax()).append( + view.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterMaterializedViewAction::AlterTableAction(action) => { + build_alter_foreign_table_action(action) + } } - if let Some(token) = tablespace.tablespace_token() { +} + +fn build_alter_aggregate<'a>(stmt: &ast::AlterAggregate) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.aggregate_token() { doc = doc .append(Doc::space()) .append(leading_comments_token(&token)) - .append(Doc::text("tablespace")); + .append(Doc::text("aggregate")); } - if let Some(name) = tablespace.tablespace_ref() { + if let Some(aggregate) = stmt.aggregate() { doc = doc .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); + .append(leading_comments(aggregate.syntax())) + .append(build_aggregate_sig(aggregate)); } - doc -} - -fn append_constraint_options<'a>( - mut doc: Doc<'a>, - options: impl Iterator, -) -> Doc<'a> { - for option in options { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(option.syntax())) - .append(build_keyword_node(option.syntax())); + if let Some(action) = stmt.action() { + let action_comments = leading_comments(action.syntax()); + let action_doc = match action { + ast::AlterAggregateAction::AggregateRenameTo(action) => { + let target = action.aggregate_name().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterAggregateAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterAggregateAction::SetSchema(action) => build_set_schema(&action), + }; + doc = doc.append( + Doc::line_or_space() + .append(action_comments) + .append(action_doc) + .nest(2), + ); } - doc + doc.append(build_semicolon(stmt.semicolon_token())).group() } -fn build_foreign_key_constraint<'a>(constraint: ast::ForeignKeyConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(foreign) = constraint.foreign_token() { +fn build_alter_collation<'a>(stmt: &ast::AlterCollation) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.collation_token() { doc = doc - .append(leading_comments_token(&foreign)) - .append(Doc::text("foreign")); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("collation")); } - if let Some(key) = constraint.key_token() { + if let Some(collation) = stmt.collation_ref() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&key)) - .append(Doc::text("key")); + .append(leading_comments(collation.syntax())); + if let Some(path) = collation.path_ref() { + doc = doc.append(build_path_ref(&path)); + } } - if let Some(columns) = constraint.from_columns() { + if let Some(action) = stmt.action() { + let action_comments = leading_comments(action.syntax()); + let action_doc = match action { + ast::AlterCollationAction::CollationRenameTo(action) => { + let target = action.collation().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterCollationAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterCollationAction::RefreshVersion(action) => build_refresh_version(action), + ast::AlterCollationAction::SetSchema(action) => build_set_schema(&action), + }; + doc = doc.append( + Doc::line_or_space() + .append(action_comments) + .append(action_doc) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() +} + +fn build_alter_conversion<'a>(stmt: &ast::AlterConversion) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.conversion_token() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_foreign_key_column_list(columns)); + .append(leading_comments_token(&token)) + .append(Doc::text("conversion")); } - if let Some(references) = constraint.references_token() { + if let Some(conversion) = stmt.conversion_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments_token(&references)) - .append(Doc::text("references")); - } - if let Some(table) = constraint.table_name_ref() { - if let Some(path) = table.path_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(table.syntax())) - .append(build_path_ref(&path)); + .append(Doc::space()) + .append(leading_comments(conversion.syntax())); + if let Some(path) = conversion.path_ref() { + doc = doc.append(build_path_ref(&path)); } } - if let Some(columns) = constraint.to_columns() { + if let Some(action) = stmt.action() { + let action_comments = leading_comments(action.syntax()); + let action_doc = match action { + ast::AlterConversionAction::ConversionRenameTo(action) => { + let target = action.conversion().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(action.to_token(), target) + } + ast::AlterConversionAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterConversionAction::SetSchema(action) => build_set_schema(&action), + }; + doc = doc.append( + Doc::line_or_space() + .append(action_comments) + .append(action_doc) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() +} + +fn build_rename_to<'a>(to_token: Option, target: Option>) -> Doc<'a> { + let mut doc = Doc::text("rename"); + if let Some(token) = to_token { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_foreign_key_column_list(columns)); + .append(leading_comments_token(&token)) + .append(Doc::text("to")); } - if let Some(match_type) = constraint.match_type() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(match_type.syntax())) - .append(build_keyword_node(match_type.syntax())); + if let Some(target) = target { + doc = doc.append(Doc::space()).append(target); } - if let Some(action) = constraint.on_delete_action() { + doc +} + +fn build_set_schema<'a>(action: &ast::SetSchema) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(token) = action.schema_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(action.syntax())) - .append(build_reference_action( - action.on_token(), - action.delete_token(), - "delete", - action.ref_action(), - )); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("schema")); } - if let Some(action) = constraint.on_update_action() { + if let Some(schema) = action.schema_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(action.syntax())) - .append(build_reference_action( - action.on_token(), - action.update_token(), - "update", - action.ref_action(), - )); + .append(Doc::space()) + .append(leading_comments(schema.syntax())) + .append(build_name(schema.syntax())); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() + doc } -fn build_foreign_key_column_list<'a>(list: ast::ForeignKeyColumnList) -> Doc<'a> { - let suffix = list.period_column().map(|period| { - let mut doc = Doc::space() - .append(leading_comments(period.syntax())) - .append(Doc::text("period")); - if let Some(name) = period.name() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); - } - doc - }); - build_column_names( - list.l_paren_token(), - list.column_name_refs(), - suffix, - list.r_paren_token(), - ) +fn build_refresh_version<'a>(action: ast::RefreshVersion) -> Doc<'a> { + let mut doc = Doc::text("refresh"); + if let Some(token) = action.version_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("version")); + } + doc } -fn build_reference_action<'a>( - on: Option, - kind_token: Option, - kind: &'static str, - action: Option, -) -> Doc<'a> { - let mut doc = on - .map(|token| leading_comments_token(&token).append(Doc::text("on"))) - .unwrap_or_else(Doc::nil); - if let Some(token) = kind_token { +fn build_alter_database<'a>(stmt: &ast::AlterDatabase) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.database_token() { doc = doc .append(Doc::space()) .append(leading_comments_token(&token)) - .append(Doc::text(kind)); + .append(Doc::text("database")); } - let Some(action) = action else { - return doc; - }; - doc = doc - .append(Doc::space()) - .append(leading_comments(action.syntax())); - match action { - ast::RefAction::SetNullColumns(action) => { - if let Some(set) = action.set_token() { - doc = doc - .append(leading_comments_token(&set)) - .append(Doc::text("set")); - } - if let Some(null) = action.null_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&null)) - .append(Doc::text("null")); - } - if let Some(columns) = action.column_ref_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); - } - doc - } - ast::RefAction::SetDefaultColumns(action) => { - if let Some(set) = action.set_token() { - doc = doc - .append(leading_comments_token(&set)) - .append(Doc::text("set")); - } - if let Some(default) = action.default_token() { + if let Some(database) = stmt.database_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(database.syntax())) + .append(build_name(database.syntax())); + } + if let Some(action) = stmt.action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_alter_database_action(action)) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() +} + +fn build_alter_database_action<'a>(action: ast::AlterDatabaseAction) -> Doc<'a> { + match action { + ast::AlterDatabaseAction::DatabaseOptionList(action) => build_database_option_list(action), + ast::AlterDatabaseAction::DatabaseRenameTo(action) => { + let target = action + .database() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); + build_rename_to(action.to_token(), target) + } + ast::AlterDatabaseAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterDatabaseAction::RefreshCollationVersion(action) => { + let mut doc = Doc::text("refresh"); + if let Some(token) = action.collation_token() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&default)) - .append(Doc::text("default")); + .append(leading_comments_token(&token)) + .append(Doc::text("collation")); } - if let Some(columns) = action.column_ref_list() { + if let Some(token) = action.version_token() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); + .append(leading_comments_token(&token)) + .append(Doc::text("version")); } doc } - action => doc.append(build_keyword_node(action.syntax())), + ast::AlterDatabaseAction::ResetConfigParam(action) => build_reset_config_param(&action), + ast::AlterDatabaseAction::SetConfigParam(action) => build_set_config_param(&action), + ast::AlterDatabaseAction::SetTablespace(action) => build_set_tablespace(action), } } -fn build_exclude_constraint<'a>(constraint: ast::ExcludeConstraint) -> Doc<'a> { - let mut doc = build_constraint_name_clause(constraint.constraint_name_clause()); - if let Some(exclude) = constraint.exclude_token() { +fn build_reset_config_param<'a>(action: &ast::ResetConfigParam) -> Doc<'a> { + let mut doc = Doc::text("reset"); + if let Some(token) = action.all_token() { doc = doc - .append(leading_comments_token(&exclude)) - .append(Doc::text("exclude")); - } - if let Some(method) = constraint.constraint_index_method() { + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("all")); + } else if let Some(parameter) = action.config_parameter_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(method.syntax())) - .append(Doc::text("using")); - if let Some(name) = method.access_method_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(name.syntax())) - .append(build_name(name.syntax())); + .append(leading_comments(parameter.syntax())); + if let Some(path) = parameter.path_ref() { + doc = doc.append(build_path_ref(&path)); } } - if let Some(list) = constraint.constraint_exclusion_list() { + doc +} + +fn build_set_tablespace<'a>(action: ast::SetTablespace) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(token) = action.tablespace_token() { doc = doc .append(Doc::space()) - .append(leading_comments(list.syntax())) - .append(build_constraint_exclusion_list(list)); + .append(leading_comments_token(&token)) + .append(Doc::text("tablespace")); } - if let Some(include) = constraint.constraint_include_clause() { + if let Some(tablespace) = action.tablespace_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(include.syntax())) - .append(build_constraint_include_clause(include)); + .append(Doc::space()) + .append(leading_comments(tablespace.syntax())) + .append(build_name(tablespace.syntax())); } - if let Some(with_params) = constraint.with_params() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(with_params.syntax())) - .append(build_with_params(with_params)); + doc +} + +fn build_database_option_list<'a>(list: ast::DatabaseOptionList) -> Doc<'a> { + let mut doc = list + .with_token() + .map(|token| leading_comments_token(&token).append(Doc::text("with"))); + for option in list.database_options() { + let option_doc = leading_comments(option.syntax()).append(build_database_option(option)); + doc = Some(match doc { + Some(doc) => doc.append(Doc::line_or_space().append(option_doc)), + None => option_doc, + }); } - if let Some(tablespace) = constraint.constraint_index_tablespace() { + doc.unwrap_or_else(Doc::nil).group() +} + +fn build_database_option<'a>(option: ast::DatabaseOption) -> Doc<'a> { + match option { + ast::DatabaseOption::DatabaseOptionConnectionLimit(option) => { + let mut doc = Doc::text("connection"); + if let Some(token) = option.limit_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("limit")); + } + build_database_option_assignment( + doc, + option.eq_token(), + option.literal(), + option.default_token(), + option + .config_value_name() + .map(|value| build_commented_name(&value)), + ) + } + ast::DatabaseOption::DatabaseOptionEncoding(option) => build_database_option_assignment( + Doc::text("encoding"), + option.eq_token(), + option.literal(), + option.default_token(), + option + .config_value_name() + .map(|value| build_commented_name(&value)), + ), + ast::DatabaseOption::DatabaseOptionGeneric(option) => build_database_option_assignment( + build_name(option.syntax()), + option.eq_token(), + option.literal(), + option.default_token(), + option + .config_value_name() + .map(|value| build_commented_name(&value)), + ), + ast::DatabaseOption::DatabaseOptionOwner(option) => build_database_option_assignment( + Doc::text("owner"), + option.eq_token(), + option.literal(), + option.default_token(), + option.role_ref().map(|value| build_commented_name(&value)), + ), + ast::DatabaseOption::DatabaseOptionTablespace(option) => build_database_option_assignment( + Doc::text("tablespace"), + option.eq_token(), + option.literal(), + option.default_token(), + option + .tablespace_ref() + .map(|value| build_commented_name(&value)), + ), + ast::DatabaseOption::DatabaseOptionTemplate(option) => build_database_option_assignment( + Doc::text("template"), + option.eq_token(), + option.literal(), + option.default_token(), + option + .database_ref() + .map(|value| build_commented_name(&value)), + ), + } +} + +fn build_commented_name<'a>(node: &impl AstNode) -> Doc<'a> { + leading_comments(node.syntax()).append(build_name(node.syntax())) +} + +fn build_database_option_assignment<'a>( + mut doc: Doc<'a>, + eq_token: Option, + literal: Option, + default_token: Option, + named_value: Option>, +) -> Doc<'a> { + if let Some(token) = eq_token { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(tablespace.syntax())) - .append(build_constraint_index_tablespace(tablespace)); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("=")); } - if let Some(where_clause) = constraint.where_condition_clause() { + if let Some(literal) = literal { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(where_clause.syntax())) - .append(build_where_condition_clause(where_clause)); + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } else if let Some(token) = default_token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("default")); + } else if let Some(value) = named_value { + doc = doc.append(Doc::space()).append(value); } - append_constraint_options(doc, constraint.constraint_options()) - .nest(2) - .group() + doc.group() } -fn build_constraint_exclusion_list<'a>(list: ast::ConstraintExclusionList) -> Doc<'a> { - let doc = list - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let items = list.constraint_exclusions().map(|exclusion| { - let mut item = exclusion.expr().map(build_expr).unwrap_or_else(Doc::nil); - if let Some(with) = exclusion.with_token() { - item = item - .append(Doc::line_or_space()) - .append(leading_comments_token(&with)) - .append(Doc::text("with")); - } - if let Some(op) = exclusion.op() { - item = item - .append(Doc::space()) - .append(leading_comments(op.syntax())) - .append(build_operator(&op)); - } else if let Some(op) = exclusion.operator_call() { - item = item +fn build_alter_default_privileges<'a>(stmt: &ast::AlterDefaultPrivileges) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.default_token(), "default"), + (stmt.privileges_token(), "privileges"), + ] { + if let Some(token) = token { + doc = doc .append(Doc::space()) - .append(leading_comments(op.syntax())) - .append(build_operator_call(&op)); + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } - ( - leading_comments(exclusion.syntax()).append(item.nest(2).group()), - exclusion.syntax().clone(), - ) - }); - let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - if let Some(r_paren) = list.r_paren_token() { - body = body.append(comments_before(r_paren)); } - doc.append(wrap_body(body)).append(Doc::text(")")).group() + if let Some(for_roles) = stmt.for_roles() { + let mut clause = build_keyword_tokens([ + (for_roles.for_token(), "for"), + (for_roles.role_token(), "role"), + (for_roles.user_token(), "user"), + ]); + if let Some(roles) = for_roles.role_ref_list() { + clause = clause + .append(Doc::line_or_space()) + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(for_roles.syntax())) + .append(clause) + .nest(2), + ); + } + if let Some(in_schemas) = stmt.in_schemas() { + let mut clause = build_keyword_tokens([ + (in_schemas.in_token(), "in"), + (in_schemas.schema_token(), "schema"), + ]); + let schemas = build_comma_separated_docs(in_schemas.schema_refs().map(|schema| { + let syntax = schema.syntax().clone(); + ( + leading_comments(&syntax).append(build_name(&syntax)), + syntax, + ) + })) + .unwrap_or_else(Doc::nil); + clause = clause.append(Doc::line_or_space().append(schemas).nest(2)); + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(in_schemas.syntax())) + .append(clause) + .nest(2), + ); + } + if let Some(action) = stmt.action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_alter_default_privileges_action(action)) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_where_condition_clause<'a>(where_clause: ast::WhereConditionClause) -> Doc<'a> { - let mut doc = Doc::text("where"); - if let Some(l_paren) = where_clause.l_paren_token() { - if comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); - } else { - doc = doc.append(comments_before(l_paren)); +fn build_privilege_target<'a>(target: ast::PrivilegeTarget) -> Doc<'a> { + match target { + ast::PrivilegeTarget::PrivilegeTargetFunctions(node) => { + build_keyword_tokens([(node.functions_token(), "functions")]) + } + ast::PrivilegeTarget::PrivilegeTargetLargeObjects(node) => build_keyword_tokens([ + (node.large_token(), "large"), + (node.objects_token(), "objects"), + ]), + ast::PrivilegeTarget::PrivilegeTargetRoutines(node) => { + build_keyword_tokens([(node.routines_token(), "routines")]) + } + ast::PrivilegeTarget::PrivilegeTargetSchemas(node) => { + build_keyword_tokens([(node.schemas_token(), "schemas")]) + } + ast::PrivilegeTarget::PrivilegeTargetSequences(node) => { + build_keyword_tokens([(node.sequences_token(), "sequences")]) + } + ast::PrivilegeTarget::PrivilegeTargetTables(node) => { + build_keyword_tokens([(node.tables_token(), "tables")]) + } + ast::PrivilegeTarget::PrivilegeTargetTypes(node) => { + build_keyword_tokens([(node.types_token(), "types")]) } } - doc = doc.append(Doc::text("(")); +} - let mut body = Doc::nil(); - if let Some(expr) = where_clause.expr() { - body = body - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); - } - if let Some(r_paren) = where_clause.r_paren_token() { - body = body.append(comments_before(r_paren)); +fn build_alter_default_privileges_action<'a>(action: ast::AlterDefaultPrivilegesAction) -> Doc<'a> { + match action { + ast::AlterDefaultPrivilegesAction::GrantDefaultPrivileges(grant) => { + let mut doc = Doc::text("grant"); + if let Some(privileges) = grant.privileges() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(privileges.syntax())) + .append(build_privileges(privileges)) + .nest(2), + ); + } + if let Some(on) = grant.on_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&on)) + .append(Doc::text("on")); + } + if let Some(target) = grant.privilege_target() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(target.syntax())) + .append(build_privilege_target(target)) + .nest(2), + ); + } + if let Some(to) = grant.to_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + if let Some(roles) = grant.role_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + if let Some(with) = grant.grant_with_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(with.syntax())) + .append(build_grant_with_clause(with)) + .nest(2), + ); + } + doc.group() + } + ast::AlterDefaultPrivilegesAction::RevokeDefaultPrivileges(revoke) => { + let mut doc = Doc::text("revoke"); + if let Some(option) = revoke.grant_option_for() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(option.syntax())) + .append(build_keyword_tokens([ + (option.grant_token(), "grant"), + (option.option_token(), "option"), + (option.for_token(), "for"), + ])) + .nest(2), + ); + } + if let Some(privileges) = revoke.privileges() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(privileges.syntax())) + .append(build_privileges(privileges)) + .nest(2), + ); + } + if let Some(on) = revoke.on_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&on)) + .append(Doc::text("on")); + } + if let Some(target) = revoke.privilege_target() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(target.syntax())) + .append(build_privilege_target(target)) + .nest(2), + ); + } + if let Some(from) = revoke.from_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&from)) + .append(Doc::text("from")); + } + if let Some(roles) = revoke.role_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); + } + if let Some(behavior) = revoke.drop_behavior() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)) + .nest(2), + ); + } + doc.group() + } } - doc.append(wrap_body(body)).append(Doc::text(")")).group() } -fn build_like_clause<'a>(like_clause: &ast::LikeClause) -> Doc<'a> { - let mut doc = Doc::text("like"); - - if let Some(relation_name) = like_clause.relation_name_ref() { +fn build_alter_domain<'a>(stmt: &ast::AlterDomain) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(domain) = stmt.domain_token() { doc = doc .append(Doc::space()) - .append(leading_comments(relation_name.syntax())); - if let Some(path) = relation_name.path_ref() { + .append(leading_comments_token(&domain)) + .append(Doc::text("domain")); + } + if let Some(domain) = stmt.domain_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(domain.syntax())); + if let Some(path) = domain.path_ref() { doc = doc.append(build_path_ref(&path)); } } - - let options: Vec> = like_clause - .like_options() - .map(|option| { + if let Some(action) = stmt.action() { + doc = doc.append( Doc::line_or_space() - .append(leading_comments(option.syntax())) - .append(build_like_option(&option)) - }) - .collect(); - if !options.is_empty() { - doc = doc.append(Doc::list(options).nest(2).group()); + .append(leading_comments(action.syntax())) + .append(build_alter_domain_action(action)) + .nest(2), + ); } - - doc + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_like_option<'a>(option: &ast::LikeOption) -> Doc<'a> { - let (keyword, property) = match option { - ast::LikeOption::ExcludingProperty(n) => ("excluding", n.table_property()), - ast::LikeOption::IncludingProperty(n) => ("including", n.table_property()), - }; - - let mut doc = Doc::text(keyword); - if let Some(property) = property { - doc = doc - .append(Doc::space()) - .append(leading_comments(property.syntax())) - .append(build_keyword_node(property.syntax())); +fn build_domain_constraint<'a>(constraint: ast::Constraint) -> Doc<'a> { + match constraint { + ast::Constraint::CheckConstraint(node) => build_check_constraint(node), + ast::Constraint::DefaultConstraint(node) => { + let mut doc = build_constraint_name_clause(node.constraint_name_clause()); + if let Some(default) = node.default_token() { + doc = doc + .append(leading_comments_token(&default)) + .append(Doc::text("default")); + } + if let Some(expr) = node.expr() { + doc = doc + .append(Doc::space()) + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)); + } + append_constraint_options(doc, node.constraint_options()) + .nest(2) + .group() + } + ast::Constraint::ExcludeConstraint(node) => build_exclude_constraint(node), + ast::Constraint::ForeignKeyConstraint(node) => build_foreign_key_constraint(node), + ast::Constraint::GeneratedConstraint(node) => build_generated_constraint(node), + ast::Constraint::NotNullConstraint(node) => { + let mut doc = build_constraint_name_clause(node.constraint_name_clause()); + doc = doc.append(build_keyword_tokens([ + (node.not_token(), "not"), + (node.null_token(), "null"), + ])); + append_constraint_options(doc, node.constraint_options()) + .nest(2) + .group() + } + ast::Constraint::NullConstraint(node) => { + let mut doc = build_constraint_name_clause(node.constraint_name_clause()); + if let Some(null) = node.null_token() { + doc = doc + .append(leading_comments_token(&null)) + .append(Doc::text("null")); + } + append_constraint_options(doc, node.constraint_options()) + .nest(2) + .group() + } + ast::Constraint::PrimaryKeyConstraint(node) => build_primary_key_constraint(node), + ast::Constraint::ReferencesConstraint(node) => build_references_constraint(node), + ast::Constraint::UniqueConstraint(node) => build_unique_constraint(node), } - doc } -fn build_values<'a>(values: &ast::Values) -> Doc<'a> { - let mut doc = Doc::nil(); - if let Some(with_clause) = values.with_clause() { - doc = doc - .append(leading_comments(with_clause.syntax())) - .append(build_with_clause(with_clause)) - .append(Doc::hard_line()); - if let Some(values_token) = values.values_token() { - doc = doc.append(leading_comments_token(&values_token)); +fn build_alter_domain_action<'a>(action: ast::AlterDomainAction) -> Doc<'a> { + match action { + ast::AlterDomainAction::AddConstraint(node) => { + let mut doc = Doc::text("add"); + if let Some(constraint) = node.constraint() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(constraint.syntax())) + .append(build_domain_constraint(constraint)) + .nest(2), + ); + } + doc.group() + } + ast::AlterDomainAction::DomainRenameTo(node) => { + let target = node.domain().map(|name| { + leading_comments(name.syntax()).append( + name.path() + .map(|path| build_path(&path)) + .unwrap_or_else(Doc::nil), + ) + }); + build_rename_to(node.to_token(), target) + } + ast::AlterDomainAction::DropConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.drop_token(), "drop"), + (node.constraint_token(), "constraint"), + ]); + if let Some(if_exists) = node.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(name) = node.constraint_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(behavior) = node.drop_behavior() { + doc = doc + .append(Doc::space()) + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)); + } + doc.group() + } + ast::AlterDomainAction::DropDefault(node) => build_keyword_tokens([ + (node.drop_token(), "drop"), + (node.default_token(), "default"), + ]), + ast::AlterDomainAction::DropNotNull(node) => build_keyword_tokens([ + (node.drop_token(), "drop"), + (node.not_token(), "not"), + (node.null_token(), "null"), + ]), + ast::AlterDomainAction::OwnerTo(node) => build_owner_to(&node), + ast::AlterDomainAction::RenameConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.rename_token(), "rename"), + (node.constraint_token(), "constraint"), + ]); + if let Some(name) = node.constraint_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + if let Some(to) = node.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&to)) + .append(Doc::text("to")); + } + if let Some(name) = node.constraint_name() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); + } + doc + } + ast::AlterDomainAction::SetDefault(node) => { + let mut doc = build_keyword_tokens([ + (node.set_token(), "set"), + (node.default_token(), "default"), + ]); + if let Some(expr) = node.expr() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(expr.syntax())) + .append(build_expr(expr)) + .nest(2), + ); + } + doc.group() + } + ast::AlterDomainAction::SetNotNull(node) => build_keyword_tokens([ + (node.set_token(), "set"), + (node.not_token(), "not"), + (node.null_token(), "null"), + ]), + ast::AlterDomainAction::SetSchema(node) => build_set_schema(&node), + ast::AlterDomainAction::ValidateConstraint(node) => { + let mut doc = build_keyword_tokens([ + (node.validate_token(), "validate"), + (node.constraint_token(), "constraint"), + ]); + if let Some(name) = node.constraint_name_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(name.syntax())); + if let Some(path) = name.path_ref() { + doc = doc.append(build_path_ref(&path)); + } + } + doc } } +} - let mut values_doc = Doc::text("values"); - if let Some(row_list) = values.row_list() { - let rows = row_list.rows().map(|row| { - ( - leading_comments(row.syntax()).append(build_row(row.clone())), - row.syntax().clone(), - ) - }); - if let Some(rows) = build_comma_separated_docs(rows) { - values_doc = values_doc.append( - Doc::space() - .append(leading_comments(row_list.syntax())) - .append(rows), - ); +fn build_alter_event_trigger<'a>(stmt: &ast::AlterEventTrigger) -> Doc<'a> { + let mut doc = Doc::text("alter"); + for (token, keyword) in [ + (stmt.event_token(), "event"), + (stmt.trigger_token(), "trigger"), + ] { + if let Some(token) = token { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text(keyword)); } } - doc = doc.append(values_doc.group()); - - if let Some(order_by) = values.order_by_clause() { + if let Some(name) = stmt.event_trigger_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(order_by.syntax())) - .append(build_order_by_clause(order_by)); + .append(Doc::space()) + .append(leading_comments(name.syntax())) + .append(build_name(name.syntax())); } - for locking in values.locking_clauses() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(locking.syntax())) - .append(build_locking_clause(locking)); + if let Some(action) = stmt.action() { + let action_comments = leading_comments(action.syntax()); + let action_doc = match action { + ast::AlterEventTriggerAction::Disable(node) => { + build_keyword_tokens([(node.disable_token(), "disable")]) + } + ast::AlterEventTriggerAction::Enable(node) => { + build_keyword_tokens([(node.enable_token(), "enable")]) + } + ast::AlterEventTriggerAction::EnableAlways(node) => build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.always_token(), "always"), + ]), + ast::AlterEventTriggerAction::EnableReplica(node) => build_keyword_tokens([ + (node.enable_token(), "enable"), + (node.replica_token(), "replica"), + ]), + ast::AlterEventTriggerAction::EventTriggerRenameTo(node) => { + let target = node + .event_trigger() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))); + build_rename_to(node.to_token(), target) + } + ast::AlterEventTriggerAction::OwnerTo(node) => build_owner_to(&node), + }; + doc = doc.append( + Doc::line_or_space() + .append(action_comments) + .append(action_doc) + .nest(2), + ); } - if let Some(limit) = values.limit_clause() { + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_alter_extension<'a>(stmt: &ast::AlterExtension) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.extension_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(limit.syntax())) - .append(build_limit_clause(limit)); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("extension")); } - if let Some(fetch) = values.fetch_clause() { + if let Some(extension) = stmt.extension_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(fetch.syntax())) - .append(build_fetch_clause(fetch)); + .append(Doc::space()) + .append(leading_comments(extension.syntax())) + .append(build_name(extension.syntax())); } - if let Some(offset) = values.offset_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(offset.syntax())) - .append(build_offset_clause(offset)); + if let Some(action) = stmt.action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_alter_extension_action(action)) + .nest(2), + ); } - - doc.append(build_semicolon(values.semicolon_token())) - .group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_row<'a>(row: ast::Row) -> Doc<'a> { - let mut doc = row - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let exprs = build_comma_separated_exprs(row.exprs()); - let has_exprs = exprs.is_some(); - let mut body = exprs.unwrap_or_else(Doc::nil); - if !has_exprs { - if let Some(r_paren) = row.r_paren_token() { - body = body.append(comments_before(r_paren)); +fn build_alter_extension_action<'a>(action: ast::AlterExtensionAction) -> Doc<'a> { + match action { + ast::AlterExtensionAction::AlterExtensionAdd(node) => { + build_alter_extension_member_action("add", node.extension_member_object()) } - } - doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); - doc -} - -fn build_table<'a>(table: &ast::Table) -> Doc<'a> { - let mut doc = Doc::nil(); - if let Some(with_clause) = table.with_clause() { - doc = doc - .append(leading_comments(with_clause.syntax())) - .append(build_with_clause(with_clause)) - .append(Doc::hard_line()); - if let Some(table_token) = table.table_token() { - doc = doc.append(leading_comments_token(&table_token)); + ast::AlterExtensionAction::AlterExtensionDrop(node) => { + build_alter_extension_member_action("drop", node.extension_member_object()) + } + ast::AlterExtensionAction::AlterExtensionUpdate(node) => { + let mut doc = Doc::text("update"); + if let Some(update_to) = node.update_to() { + let mut update_doc = Doc::text("to"); + if let Some(version) = update_to.extension_version() { + update_doc = update_doc + .append(Doc::space()) + .append(leading_comments(version.syntax())) + .append(build_name(version.syntax())); + } else if let Some(literal) = update_to.literal() { + update_doc = update_doc + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(update_to.syntax())) + .append(update_doc) + .nest(2), + ); + } + doc.group() } + ast::AlterExtensionAction::SetSchema(node) => build_set_schema(&node), } +} - let mut table_doc = Doc::text("table"); - if let Some(relation) = table.relation_name() { - table_doc = table_doc.append( +fn build_alter_extension_member_action<'a>( + keyword: &'static str, + object: Option, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(object) = object { + doc = doc.append( Doc::line_or_space() - .append(leading_comments(relation.syntax())) - .append(build_relation_name(relation)) + .append(leading_comments(object.syntax())) + .append(build_extension_member_object(object)) .nest(2), ); } - doc = doc.append(table_doc.group()); + doc.group() +} - if let Some(order_by) = table.order_by_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(order_by.syntax())) - .append(build_order_by_clause(order_by)); - } - for locking in table.locking_clauses() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(locking.syntax())) - .append(build_locking_clause(locking)); - } - if let Some(limit) = table.limit_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(limit.syntax())) - .append(build_limit_clause(limit)); - } - if let Some(fetch) = table.fetch_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(fetch.syntax())) - .append(build_fetch_clause(fetch)); +fn build_extension_member_value<'a>(prefix: Doc<'a>, value: Option>) -> Doc<'a> { + if let Some(value) = value { + prefix + .append(Doc::line_or_space().append(value).nest(2)) + .group() + } else { + prefix } - if let Some(offset) = table.offset_clause() { +} + +fn build_extension_path_value<'a>(node: &impl ast::HasPathRef) -> Doc<'a> { + leading_comments(node.syntax()).append( + node.path_ref() + .map(|path| build_path_ref(&path)) + .unwrap_or_else(Doc::nil), + ) +} + +fn build_using_method<'a>(using: ast::UsingMethod) -> Doc<'a> { + let mut doc = Doc::text("using"); + if let Some(method) = using.access_method_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(offset.syntax())) - .append(build_offset_clause(offset)); + .append(Doc::space()) + .append(leading_comments(method.syntax())) + .append(build_name(method.syntax())); } + doc +} - doc.append(build_semicolon(table.semicolon_token())).group() +fn build_cast_sig<'a>(sig: ast::CastSig) -> Doc<'a> { + let source = sig.lhs(); + let target = sig.rhs(); + let body = source + .map(|ty| leading_comments(ty.syntax()).append(build_type(ty))) + .unwrap_or_else(Doc::nil) + .append(Doc::line_or_space()) + .append( + sig.as_token() + .map(|token| leading_comments_token(&token).append(Doc::text("as"))) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::line_or_space()) + .append( + target + .map(|ty| leading_comments(ty.syntax()).append(build_type(ty))) + .unwrap_or_else(Doc::nil), + ); + sig.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append( + sig.r_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text(")")) + .group() } -fn build_relation_name<'a>(relation: ast::RelationName) -> Doc<'a> { - let mut doc = Doc::nil(); - let has_only = relation.only_token().is_some(); - if let Some(only) = relation.only_token() { - doc = doc - .append(leading_comments_token(&only)) - .append(Doc::text("only")); +fn build_custom_operator<'a>(operator: ast::CustomOp) -> Doc<'a> { + Doc::list( + operator + .tokens() + .map(|token| build_operator_token(&token)) + .collect(), + ) +} + +fn build_extension_operator<'a>(node: ast::ObjectOperator) -> Doc<'a> { + let mut doc = build_keyword_tokens([(node.operator_token(), "operator")]); + if let Some(op) = node.op() { + let op_doc = leading_comments(op.syntax()).append(build_operator(&op)); + doc = doc.append(Doc::line_or_space().append(op_doc).nest(2)); } - if let Some(l_paren) = relation.l_paren_token() { - if has_only && comment_tokens_before(l_paren.clone()).is_empty() { - doc = doc.append(Doc::space()); + let body = + build_comma_separated_docs([node.lhs(), node.rhs()].into_iter().flatten().map(|ty| { + let syntax = ty.syntax().clone(); + (leading_comments(&syntax).append(build_type(ty)), syntax) + })) + .unwrap_or_else(Doc::nil); + doc.append( + Doc::line_or_space() + .append( + node.l_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text("(")) + .append(wrap_body(body)) + .append( + node.r_paren_token() + .map(comments_before) + .unwrap_or_else(Doc::nil), + ) + .append(Doc::text(")")) + .nest(2), + ) + .group() +} + +fn build_extension_member_object<'a>(object: ast::ExtensionMemberObject) -> Doc<'a> { + match object { + ast::ExtensionMemberObject::ObjectAccessMethod(node) => build_extension_member_value( + build_keyword_tokens([ + (node.access_token(), "access"), + (node.method_token(), "method"), + ]), + node.access_method_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectAggregate(node) => build_extension_member_value( + build_keyword_tokens([(node.aggregate_token(), "aggregate")]), + node.aggregate() + .map(|value| leading_comments(value.syntax()).append(build_aggregate_sig(value))), + ), + ast::ExtensionMemberObject::ObjectCast(node) => build_extension_member_value( + build_keyword_tokens([(node.cast_token(), "cast")]), + node.cast_sig() + .map(|sig| leading_comments(sig.syntax()).append(build_cast_sig(sig))), + ), + ast::ExtensionMemberObject::ObjectCollation(node) => build_extension_member_value( + build_keyword_tokens([(node.collation_token(), "collation")]), + node.collation_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectConversion(node) => build_extension_member_value( + build_keyword_tokens([(node.conversion_token(), "conversion")]), + node.conversion_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectDatabase(node) => build_extension_member_value( + build_keyword_tokens([(node.database_token(), "database")]), + node.database_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectDomain(node) => build_extension_member_value( + build_keyword_tokens([(node.domain_token(), "domain")]), + node.domain_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectEventTrigger(node) => build_extension_member_value( + build_keyword_tokens([ + (node.event_token(), "event"), + (node.trigger_token(), "trigger"), + ]), + node.event_trigger_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectExtension(node) => build_extension_member_value( + build_keyword_tokens([(node.extension_token(), "extension")]), + node.extension_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectForeignDataWrapper(node) => build_extension_member_value( + build_keyword_tokens([ + (node.foreign_token(), "foreign"), + (node.data_token(), "data"), + (node.wrapper_token(), "wrapper"), + ]), + node.foreign_data_wrapper_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectForeignTable(node) => build_extension_member_value( + build_keyword_tokens([ + (node.foreign_token(), "foreign"), + (node.table_token(), "table"), + ]), + node.table_name_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectFunction(node) => build_extension_member_value( + build_keyword_tokens([(node.function_token(), "function")]), + node.function_sig() + .map(|sig| leading_comments(sig.syntax()).append(build_function_sig(sig))), + ), + ast::ExtensionMemberObject::ObjectIndex(node) => build_extension_member_value( + build_keyword_tokens([(node.index_token(), "index")]), + node.index_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectLanguage(node) => build_extension_member_value( + build_keyword_tokens([ + (node.procedural_token(), "procedural"), + (node.language_token(), "language"), + ]), + node.language_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectMaterializedView(node) => build_extension_member_value( + build_keyword_tokens([ + (node.materialized_token(), "materialized"), + (node.view_token(), "view"), + ]), + node.view_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectOperator(node) => build_extension_operator(node), + ast::ExtensionMemberObject::ObjectOperatorClass(node) => { + let mut doc = build_extension_member_value( + build_keyword_tokens([ + (node.operator_token(), "operator"), + (node.class_token(), "class"), + ]), + node.op_class_ref() + .map(|name| build_extension_path_value(&name)), + ); + if let Some(using) = node.using_method() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) + .nest(2), + ); + } + doc.group() } - doc = doc.append(comments_before(l_paren)); - doc = doc.append(Doc::text("(")); - } - if let Some(name) = relation.relation_name_ref() { - if has_only && relation.l_paren_token().is_none() { - doc = doc.append(Doc::space()); + ast::ExtensionMemberObject::ObjectOperatorFamily(node) => { + let mut doc = build_extension_member_value( + build_keyword_tokens([ + (node.operator_token(), "operator"), + (node.family_token(), "family"), + ]), + node.op_family_ref() + .map(|name| build_extension_path_value(&name)), + ); + if let Some(using) = node.using_method() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) + .nest(2), + ); + } + doc.group() } - doc = doc.append(leading_comments(name.syntax())); - if let Some(path) = name.path_ref() { - doc = doc.append(build_path_ref(&path)); + ast::ExtensionMemberObject::ObjectProcedure(node) => build_extension_member_value( + build_keyword_tokens([(node.procedure_token(), "procedure")]), + node.procedure_sig() + .map(|sig| leading_comments(sig.syntax()).append(build_procedure_sig(sig))), + ), + ast::ExtensionMemberObject::ObjectPublication(node) => build_extension_member_value( + build_keyword_tokens([(node.publication_token(), "publication")]), + node.publication_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectRole(node) => build_extension_member_value( + build_keyword_tokens([(node.role_token(), "role")]), + node.role_ref() + .map(|role| leading_comments(role.syntax()).append(build_role_ref(&role))), + ), + ast::ExtensionMemberObject::ObjectRoutine(node) => build_extension_member_value( + build_keyword_tokens([(node.routine_token(), "routine")]), + node.routine_sig() + .map(|sig| leading_comments(sig.syntax()).append(build_routine_sig(sig))), + ), + ast::ExtensionMemberObject::ObjectSchema(node) => build_extension_member_value( + build_keyword_tokens([(node.schema_token(), "schema")]), + node.schema_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectSequence(node) => build_extension_member_value( + build_keyword_tokens([(node.sequence_token(), "sequence")]), + node.sequence_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectServer(node) => build_extension_member_value( + build_keyword_tokens([(node.server_token(), "server")]), + node.server_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectStatistics(node) => build_extension_member_value( + build_keyword_tokens([(node.statistics_token(), "statistics")]), + node.statistics_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectSubscription(node) => build_extension_member_value( + build_keyword_tokens([(node.subscription_token(), "subscription")]), + node.subscription_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectTable(node) => build_extension_member_value( + build_keyword_tokens([(node.table_token(), "table")]), + node.table_name_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectTablespace(node) => build_extension_member_value( + build_keyword_tokens([(node.tablespace_token(), "tablespace")]), + node.tablespace_ref() + .map(|name| leading_comments(name.syntax()).append(build_name(name.syntax()))), + ), + ast::ExtensionMemberObject::ObjectTextSearchConfig(node) => build_extension_member_value( + build_keyword_tokens([ + (node.text_token(), "text"), + (node.search_token(), "search"), + (node.configuration_token(), "configuration"), + ]), + node.text_search_configuration_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectTextSearchDict(node) => build_extension_member_value( + build_keyword_tokens([ + (node.text_token(), "text"), + (node.search_token(), "search"), + (node.dictionary_token(), "dictionary"), + ]), + node.text_search_dictionary_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectTextSearchParser(node) => build_extension_member_value( + build_keyword_tokens([ + (node.text_token(), "text"), + (node.search_token(), "search"), + (node.parser_token(), "parser"), + ]), + node.text_search_parser_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectTextSearchTemplate(node) => build_extension_member_value( + build_keyword_tokens([ + (node.text_token(), "text"), + (node.search_token(), "search"), + (node.template_token(), "template"), + ]), + node.text_search_template_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectTransform(node) => { + let mut doc = build_keyword_tokens([ + (node.transform_token(), "transform"), + (node.for_token(), "for"), + ]); + if let Some(ty) = node.ty() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(ty.syntax())) + .append(build_type(ty)) + .nest(2), + ); + } + if let Some(token) = node.language_token() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments_token(&token)) + .append(Doc::text("language")); + } + if let Some(language) = node.language_ref() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(language.syntax())) + .append(build_name(language.syntax())); + } + doc.group() } + ast::ExtensionMemberObject::ObjectType(node) => build_extension_member_value( + build_keyword_tokens([(node.type_token(), "type")]), + node.type_name_ref() + .map(|name| build_extension_path_value(&name)), + ), + ast::ExtensionMemberObject::ObjectView(node) => build_extension_member_value( + build_keyword_tokens([(node.view_token(), "view")]), + node.view_ref() + .map(|name| build_extension_path_value(&name)), + ), } - if let Some(r_paren) = relation.r_paren_token() { - doc = doc.append(comments_before(r_paren)); - doc = doc.append(Doc::text(")")); +} + +fn build_alter_publication<'a>(stmt: &ast::AlterPublication) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.publication_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("publication")); } - if let Some(star) = relation.star_token() { + if let Some(publication) = stmt.publication_ref() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&star)) - .append(Doc::text("*")); + .append(leading_comments(publication.syntax())) + .append(build_name(publication.syntax())); } - doc + if let Some(action) = stmt.action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_alter_publication_action(action)) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() } -fn build_select_into<'a>(select_into: &ast::SelectInto) -> Doc<'a> { - let mut select_body = Doc::nil(); - if let Some(select_clause) = select_into.select_clause() { - match select_clause.select_quantifier() { - Some(ast::SelectQuantifier::DistinctClause(distinct_clause)) => { - select_body = select_body - .append(leading_comments(distinct_clause.syntax())) - .append(Doc::text("distinct")); - if let Some(distinct_on) = distinct_clause.distinct_on() { - select_body = select_body - .append(Doc::space()) - .append(leading_comments(distinct_on.syntax())) - .append(build_distinct_on(distinct_on)); - } - select_body = select_body.append(Doc::space()); +fn build_alter_publication_action<'a>(action: ast::AlterPublicationAction) -> Doc<'a> { + match action { + ast::AlterPublicationAction::AddPublicationObjects(action) => { + build_publication_object_action("add", action.publication_objects()) + } + ast::AlterPublicationAction::DropPublicationObjects(action) => { + build_publication_object_action("drop", action.publication_objects()) + } + ast::AlterPublicationAction::SetPublicationObjects(action) => { + build_publication_object_action("set", action.publication_objects()) + } + ast::AlterPublicationAction::SetAllPublicationObjectList(action) => { + let mut doc = Doc::text("set"); + if let Some(objects) = build_all_publication_objects(action.all_publication_objects()) { + doc = doc.append(Doc::line_or_space().append(objects).nest(2)); } - Some(ast::SelectQuantifier::All(all)) => { - select_body = select_body - .append(leading_comments(all.syntax())) - .append(Doc::text("all")) - .append(Doc::space()); + if let Some(except) = action.except_table_clause() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(except.syntax())) + .append(build_except_table_clause(except)); } - None => (), - } - if let Some(target_list) = select_clause.target_list() { - select_body = select_body - .append(leading_comments(target_list.syntax())) - .append(Doc::list( - Itertools::intersperse( - target_list.targets().flat_map(build_target), - Doc::text(",").append(Doc::line_or_space()), - ) - .collect(), - )); + doc.group() } - } - let mut doc = Doc::nil(); - if let Some(with_clause) = select_into.with_clause() { - doc = doc - .append(leading_comments(with_clause.syntax())) - .append(build_with_clause(with_clause)) - .append(Doc::hard_line()); - } - if select_into.with_clause().is_some() { - if let Some(select_clause) = select_into.select_clause() { - doc = doc.append(leading_comments(select_clause.syntax())); + ast::AlterPublicationAction::SetOptions(action) => build_set_options(&action), + ast::AlterPublicationAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterPublicationAction::PublicationRenameTo(action) => { + let mut doc = Doc::text("rename"); + if let Some(token) = action.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("to")); + } + if let Some(publication) = action.publication() { + doc = doc + .append(Doc::space()) + .append(leading_comments(publication.syntax())) + .append(build_name(publication.syntax())); + } + doc } } - doc = doc.append( - Doc::text("select") - .append(Doc::line_or_space().append(select_body).nest(2)) - .group(), - ); +} - if let Some(into) = select_into.into_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(into.syntax())) - .append(build_into_clause(into)); - } - if let Some(from) = select_into.from_clause() { - doc = doc - .group() - .append(Doc::line_or_space()) - .append(leading_comments(from.syntax())) - .append(build_from_clause(from)); - } - if let Some(where_clause) = select_into.where_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(where_clause.syntax())) - .append(build_where_clause(where_clause)); +fn build_publication_object_action<'a>( + keyword: &'static str, + objects: impl Iterator, +) -> Doc<'a> { + let mut doc = Doc::text(keyword); + if let Some(objects) = build_publication_objects(objects) { + doc = doc.append(Doc::line_or_space().append(objects).nest(2)); } - if let Some(group) = select_into.group_by_clause() { + doc.group() +} + +fn build_set_options<'a>(options: &ast::SetOptions) -> Doc<'a> { + let mut doc = Doc::text("set"); + if let Some(attributes) = options.attribute_list() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(group.syntax())) - .append(build_select_group_by_clause(group)); + .append(Doc::space()) + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); } - if let Some(having) = select_into.having_clause() { + doc +} + +fn build_owner_to<'a>(owner: &ast::OwnerTo) -> Doc<'a> { + let mut doc = Doc::text("owner"); + if let Some(token) = owner.to_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(having.syntax())) - .append(build_having_clause(having)); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("to")); } - if let Some(window) = select_into.window_clause() { + if let Some(role) = owner.role_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(window.syntax())) - .append(build_window_clause(window)); + .append(Doc::space()) + .append(leading_comments(role.syntax())) + .append(build_name(role.syntax())); } - if let Some(order_by) = select_into.order_by_clause() { + doc +} + +fn build_create_subscription<'a>(stmt: &ast::CreateSubscription) -> Doc<'a> { + let mut doc = Doc::text("create"); + if let Some(token) = stmt.subscription_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(order_by.syntax())) - .append(build_order_by_clause(order_by)); + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("subscription")); } - for locking in select_into.locking_clauses() { + if let Some(subscription) = stmt.subscription() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(locking.syntax())) - .append(build_locking_clause(locking)); + .append(Doc::space()) + .append(leading_comments(subscription.syntax())) + .append(build_name(subscription.syntax())); } - if let Some(limit) = select_into.limit_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(limit.syntax())) - .append(build_limit_clause(limit)); + if let Some(source) = stmt.source() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(source.syntax())) + .append(build_subscription_source(source)) + .nest(2), + ); } - if let Some(offset) = select_into.offset_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(offset.syntax())) - .append(build_offset_clause(offset)); + if let Some(token) = stmt.publication_token() { + let mut publication_doc = leading_comments_token(&token).append(Doc::text("publication")); + if let Some(publications) = build_publication_refs(stmt.publication_refs()) { + publication_doc = publication_doc + .append(Doc::space()) + .append(publications.nest(2)); + } + doc = doc.append(Doc::line_or_space().append(publication_doc).nest(2)); } - if let Some(filter) = select_into.filter_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(filter.syntax())) - .append(build_filter_clause(filter)); + if let Some(params) = stmt.with_params() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(params.syntax())) + .append(build_with_params(params)) + .nest(2), + ); } - doc.append(build_semicolon(select_into.semicolon_token())) - .group() + doc.append(build_semicolon(stmt.semicolon_token())).group() } -fn build_with_clause<'a>(with_clause: ast::WithClause) -> Doc<'a> { - let mut doc = Doc::text("with"); - if let Some(recursive) = with_clause.recursive_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&recursive)) - .append(Doc::text("recursive")); +fn build_subscription_source<'a>(source: ast::SubscriptionSource) -> Doc<'a> { + match source { + ast::SubscriptionSource::ConnectionClause(source) => { + let mut doc = Doc::text("connection"); + if let Some(literal) = source.literal() { + doc = doc + .append(Doc::space()) + .append(leading_comments(literal.syntax())) + .append(build_literal(literal)); + } + doc + } + ast::SubscriptionSource::ServerClause(source) => { + let mut doc = Doc::text("server"); + if let Some(server) = source.server_ref() { + doc = doc + .append(Doc::space()) + .append(leading_comments(server.syntax())) + .append(build_name(server.syntax())); + } + doc + } } - let tables = with_clause.with_tables().map(|table| { +} + +fn build_publication_refs<'a>( + publications: impl Iterator, +) -> Option> { + build_comma_separated_docs(publications.map(|publication| { ( - leading_comments(table.syntax()).append(build_with_table(table.clone())), - table.syntax().clone(), + leading_comments(publication.syntax()).append(build_name(publication.syntax())), + publication.syntax().clone(), ) - }); - if let Some(tables) = build_comma_separated_docs(tables) { - doc = doc.append(Doc::space()).append(tables); - } - doc + })) } -fn build_with_table<'a>(table: ast::WithTable) -> Doc<'a> { - let mut doc = table - .name() - .map(|name| build_name(name.syntax())) - .unwrap_or_else(Doc::nil); - if let Some(columns) = table.column_list() { - doc = doc - .append(leading_comments(columns.syntax())) - .append(build_cte_column_list(columns)); - } - if let Some(as_token) = table.as_token() { +fn build_alter_subscription<'a>(stmt: &ast::AlterSubscription) -> Doc<'a> { + let mut doc = Doc::text("alter"); + if let Some(token) = stmt.subscription_token() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); + .append(leading_comments_token(&token)) + .append(Doc::text("subscription")); } - if let Some(materialized) = table.materialized_option() { + if let Some(subscription) = stmt.subscription_ref() { doc = doc .append(Doc::space()) - .append(leading_comments(materialized.syntax())) - .append(match materialized { - ast::MaterializedOption::Materialized(_) => Doc::text("materialized"), - ast::MaterializedOption::NotMaterialized(not_materialized) => Doc::text("not") + .append(leading_comments(subscription.syntax())) + .append(build_name(subscription.syntax())); + } + if let Some(action) = stmt.action() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(action.syntax())) + .append(build_alter_subscription_action(action)) + .nest(2), + ); + } + doc.append(build_semicolon(stmt.semicolon_token())).group() +} + +fn build_alter_subscription_action<'a>(action: ast::AlterSubscriptionAction) -> Doc<'a> { + match action { + ast::AlterSubscriptionAction::ConnectionClause(action) => { + build_subscription_source(ast::SubscriptionSource::ConnectionClause(action)) + } + ast::AlterSubscriptionAction::ServerClause(action) => { + build_subscription_source(ast::SubscriptionSource::ServerClause(action)) + } + ast::AlterSubscriptionAction::SetOptions(action) => build_set_options(&action), + ast::AlterSubscriptionAction::AddPublication(action) => { + build_subscription_publication_action( + "add", + action.publication_token(), + action.publication_refs(), + action.with_params(), + ) + } + ast::AlterSubscriptionAction::SetPublication(action) => { + build_subscription_publication_action( + "set", + action.publication_token(), + action.publication_refs(), + action.with_params(), + ) + } + ast::AlterSubscriptionAction::DropSubscriptionPublication(action) => { + build_subscription_publication_action( + "drop", + action.publication_token(), + action.publication_refs(), + action.with_params(), + ) + } + ast::AlterSubscriptionAction::RefreshPublication(action) => { + let mut doc = Doc::text("refresh").append(Doc::space()); + if let Some(token) = action.publication_token() { + doc = doc + .append(leading_comments_token(&token)) + .append(Doc::text("publication")); + } + if let Some(params) = action.with_params() { + doc = doc + .append(Doc::line_or_space()) + .append(leading_comments(params.syntax())) + .append(build_with_params(params)); + } + doc.group() + } + ast::AlterSubscriptionAction::EnableSubscription(_) => Doc::text("enable"), + ast::AlterSubscriptionAction::DisableSubscription(_) => Doc::text("disable"), + ast::AlterSubscriptionAction::SkipSubscription(action) => { + let mut doc = Doc::text("skip"); + if let Some(attributes) = action.attribute_list() { + doc = doc .append(Doc::space()) - .append( - not_materialized - .materialized_token() - .map(|token| leading_comments_token(&token)) - .unwrap_or_else(Doc::nil), - ) - .append(Doc::text("materialized")), - }); + .append(leading_comments(attributes.syntax())) + .append(build_attribute_list(&attributes)); + } + doc + } + ast::AlterSubscriptionAction::OwnerTo(action) => build_owner_to(&action), + ast::AlterSubscriptionAction::SubscriptionRenameTo(action) => { + let mut doc = Doc::text("rename"); + if let Some(token) = action.to_token() { + doc = doc + .append(Doc::space()) + .append(leading_comments_token(&token)) + .append(Doc::text("to")); + } + if let Some(subscription) = action.subscription() { + doc = doc + .append(Doc::space()) + .append(leading_comments(subscription.syntax())) + .append(build_name(subscription.syntax())); + } + doc + } } - if let Some(l_paren) = table.l_paren_token() { +} + +fn build_subscription_publication_action<'a>( + keyword: &'static str, + publication_token: Option, + publications: impl Iterator, + params: Option, +) -> Doc<'a> { + let mut doc = Doc::text(keyword).append(Doc::space()); + if let Some(token) = publication_token { doc = doc - .append(Doc::space()) - .append(leading_comments_token(&l_paren)) - .append(Doc::text("(")); + .append(leading_comments_token(&token)) + .append(Doc::text("publication")); } - let mut body = table - .query() - .map(|query| leading_comments(query.syntax()).append(build_with_query(query))) - .unwrap_or_else(Doc::nil); - if let Some(r_paren) = table.r_paren_token() { - body = body.append(comments_before(r_paren)); + if let Some(publications) = build_publication_refs(publications) { + doc = doc.append(Doc::space()).append(publications.nest(2)); } - doc = doc - .append(Doc::hard_line().append(body).nest(2)) - .append(Doc::hard_line()) - .append(Doc::text(")")); - if let Some(search) = table.search_clause() { + if let Some(params) = params { doc = doc .append(Doc::line_or_space()) - .append(leading_comments(search.syntax())) - .append(build_search_clause(search)); + .append(leading_comments(params.syntax())) + .append(build_with_params(params)); } - if let Some(cycle) = table.cycle_clause() { + doc.group() +} + +fn build_drop_access_method<'a>(stmt: &ast::DropAccessMethod) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.access_token(), "access"), + (stmt.method_token(), "method"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(cycle.syntax())) - .append(build_cycle_clause(cycle)); + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc.group() + if let Some(method) = stmt.access_method_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(method.syntax())) + .append(build_name(method.syntax())) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_search_clause<'a>(search: ast::SearchClause) -> Doc<'a> { - let mut doc = Doc::text("search"); - if let Some(order) = search.search_order() { +fn build_drop_aggregate<'a>(stmt: &ast::DropAggregate) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.aggregate_token(), "aggregate"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(order.syntax())) - .append(match order { - ast::SearchOrder::BreadthFirst(first) => Doc::text("breadth") - .append(Doc::space()) - .append( - first - .first_token() - .map(|token| leading_comments_token(&token)) - .unwrap_or_else(Doc::nil), - ) - .append(Doc::text("first")), - ast::SearchOrder::DepthFirst(first) => Doc::text("depth") - .append(Doc::space()) - .append( - first - .first_token() - .map(|token| leading_comments_token(&token)) - .unwrap_or_else(Doc::nil), - ) - .append(Doc::text("first")), - }); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(by_token) = search.by_token() { + if let Some(aggregates) = build_comma_separated_docs(stmt.aggregates().map(|aggregate| { + let syntax = aggregate.syntax().clone(); + ( + leading_comments(&syntax).append(build_aggregate_sig(aggregate)), + syntax, + ) + })) { + doc = doc.append(Doc::line_or_space().append(aggregates).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_cast<'a>(stmt: &ast::DropCast) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.cast_token(), "cast")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&by_token)) - .append(Doc::text("by")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(columns) = search.columns() { + if let Some(sig) = stmt.cast_sig() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_name_refs(columns.column_name_refs())); + .append(leading_comments(sig.syntax())) + .append(build_cast_sig(sig)); } - if let Some(set_column) = search.set_column() { + if let Some(behavior) = stmt.drop_behavior() { doc = doc .append(Doc::space()) - .append(leading_comments(set_column.syntax())) - .append(build_search_set_column(set_column)); + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)); } - doc.group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_search_set_column<'a>(set_column: ast::SearchSetColumn) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(column) = set_column.column_name_ref() { +fn build_drop_collation<'a>(stmt: &ast::DropCollation) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.collation_token(), "collation"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(column.syntax())) - .append(build_name(column.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc + if let Some(collations) = build_comma_separated_docs(stmt.collation_refs().map(|collation| { + let syntax = collation.syntax().clone(); + (build_extension_path_value(&collation), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(collations).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_cycle_clause<'a>(cycle: ast::CycleClause) -> Doc<'a> { - let mut doc = Doc::text("cycle"); - if let Some(columns) = cycle.columns() { +fn build_drop_conversion<'a>(stmt: &ast::DropConversion) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.conversion_token(), "conversion"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_name_refs(columns.column_name_refs())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(set_column) = cycle.set_column() { + if let Some(conversion) = stmt.conversion_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&conversion)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_database<'a>(stmt: &ast::DropDatabase) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.database_token(), "database"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(set_column.syntax())) - .append(build_cycle_set_column(set_column)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(path) = cycle.path() { + if let Some(database) = stmt.database_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(database.syntax())) + .append(build_name(database.syntax())) + .nest(2), + ); + } + if let Some(force_clause) = stmt.drop_database_force_clause() { doc = doc - .append(Doc::space()) - .append(leading_comments(path.syntax())) - .append(build_cycle_path(path)); + .append(Doc::line_or_space()) + .append(leading_comments(force_clause.syntax())) + .append(build_drop_database_force_clause(force_clause)); } - doc.group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_cycle_set_column<'a>(set_column: ast::CycleSetColumn) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(column) = set_column.column_name_ref() { +fn build_drop_database_force_clause<'a>(clause: ast::DropDatabaseForceClause) -> Doc<'a> { + let has_with = clause.with_token().is_some(); + let mut doc = if has_with { + Doc::text("with") + } else { + Doc::nil() + }; + if let Some(l_paren) = clause.l_paren_token() { + if has_with { + doc = doc.append(Doc::space()); + } doc = doc - .append(Doc::space()) - .append(leading_comments(column.syntax())) - .append(build_name(column.syntax())); + .append(leading_comments_token(&l_paren)) + .append(Doc::text("(")); } - if let Some(column_to) = set_column.column_to() { - doc = doc - .append(Doc::space()) - .append(leading_comments(column_to.syntax())) - .append(build_cycle_column_to(column_to)); + if let Some(forces) = build_comma_separated_docs(clause.forces().map(|force| { + let syntax = force.syntax().clone(); + ( + leading_comments(&syntax).append(if force.force_token().is_some() { + Doc::text("force") + } else { + Doc::nil() + }), + syntax, + ) + })) { + doc = doc.append(forces); + } + if let Some(r_paren) = clause.r_paren_token() { + doc = doc.append(comments_before(r_paren)).append(Doc::text(")")); } doc } -fn build_cycle_column_to<'a>(column_to: ast::CycleColumnTo) -> Doc<'a> { - let mut doc = Doc::text("to"); - if let Some(expr) = column_to.expr() { +fn build_drop_domain<'a>(stmt: &ast::DropDomain) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.domain_token(), "domain")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(default) = column_to.default() { - doc = doc - .append(Doc::space()) - .append(leading_comments(default.syntax())) - .append(Doc::text("default")); - if let Some(expr) = default.expr() { - doc = doc - .append(Doc::space()) - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); - } + if let Some(domains) = build_comma_separated_docs(stmt.domain_refs().map(|domain| { + let syntax = domain.syntax().clone(); + (build_extension_path_value(&domain), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(domains).nest(2)); } - doc + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_cycle_path<'a>(path: ast::CyclePath) -> Doc<'a> { - let mut doc = Doc::text("using"); - if let Some(column) = path.column_name_ref() { +fn build_drop_event_trigger<'a>(stmt: &ast::DropEventTrigger) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.event_token(), "event"), + (stmt.trigger_token(), "trigger"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(column.syntax())) - .append(build_name(column.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc + if let Some(trigger) = stmt.event_trigger_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(trigger.syntax())) + .append(build_name(trigger.syntax())) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_column_name_refs<'a>(columns: impl Iterator) -> Doc<'a> { - let columns = columns.map(|column| { +fn build_drop_extension<'a>(stmt: &ast::DropExtension) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.extension_token(), "extension"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(extensions) = build_comma_separated_docs(stmt.extension_refs().map(|extension| { + let syntax = extension.syntax().clone(); ( - leading_comments(column.syntax()).append(build_name(column.syntax())), - column.syntax().clone(), + leading_comments(&syntax).append(build_name(&syntax)), + syntax, ) - }); - build_comma_separated_docs(columns).unwrap_or_else(Doc::nil) + })) { + doc = doc.append(Doc::line_or_space().append(extensions).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_cte_column_list<'a>(columns: ast::ColumnList) -> Doc<'a> { - let mut doc = columns - .l_paren_token() - .map(comments_before) - .unwrap_or_else(Doc::nil) - .append(Doc::text("(")); - let names = columns.column_names().map(|name| { - ( - leading_comments(name.syntax()).append(build_name(name.syntax())), - name.syntax().clone(), - ) - }); - let mut body = build_comma_separated_docs(names).unwrap_or_else(Doc::nil); - if let Some(r_paren) = columns.r_paren_token() { - body = body.append(comments_before(r_paren)); +fn build_drop_foreign_data_wrapper<'a>(stmt: &ast::DropForeignDataWrapper) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.foreign_token(), "foreign"), + (stmt.data_token(), "data"), + (stmt.wrapper_token(), "wrapper"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc = doc.append(wrap_body(body)).append(Doc::text(")")).group(); - doc + if let Some(wrappers) = + build_comma_separated_docs(stmt.foreign_data_wrapper_refs().map(|wrapper| { + let syntax = wrapper.syntax().clone(); + ( + leading_comments(&syntax).append(build_name(&syntax)), + syntax, + ) + })) + { + doc = doc.append(Doc::line_or_space().append(wrappers).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_with_query<'a>(query: ast::WithQuery) -> Doc<'a> { - match query { - ast::WithQuery::CompoundSelect(select) => build_compound_select(&select), - ast::WithQuery::ParenSelect(select) => build_paren_select(select), - ast::WithQuery::Select(select) => build_select_doc(&select), - ast::WithQuery::Table(table) => build_table(&table), - ast::WithQuery::Values(values) => build_values(&values), - ast::WithQuery::Delete(delete) => build_delete(&delete), - ast::WithQuery::Insert(insert) => build_insert(&insert), - ast::WithQuery::Merge(merge) => build_merge(&merge), - ast::WithQuery::Update(update) => build_update(&update), +fn build_drop_foreign_table<'a>(stmt: &ast::DropForeignTable) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.foreign_token(), "foreign"), + (stmt.table_token(), "table"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } + if let Some(tables) = build_comma_separated_docs(stmt.table_name_refs().map(|table| { + let syntax = table.syntax().clone(); + (build_extension_path_value(&table), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(tables).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_distinct_on<'a>(distinct_on: ast::DistinctOn) -> Doc<'a> { - let mut doc = Doc::text("on"); - if let Some(l_paren) = distinct_on.l_paren_token() { +fn build_drop_function<'a>(stmt: &ast::DropFunction) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.function_token(), "function"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&l_paren)) - .append(Doc::text("(")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - let exprs: Vec<_> = distinct_on.exprs().collect(); - let has_exprs = !exprs.is_empty(); - let mut body = build_comma_separated_exprs(exprs.into_iter()).unwrap_or_else(Doc::nil); - if !has_exprs { - if let Some(r_paren) = distinct_on.r_paren_token() { - body = body.append(comments_before(r_paren)); + if let Some(list) = stmt.function_sig_list() { + if let Some(functions) = build_comma_separated_docs(list.function_sigs().map(|function| { + let syntax = function.syntax().clone(); + ( + leading_comments(&syntax).append(build_function_sig(function)), + syntax, + ) + })) { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(functions) + .nest(2), + ); } } - doc.append(wrap_body(body)).append(Doc::text(")")).group() + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_having_clause<'a>(having: ast::HavingClause) -> Doc<'a> { - let mut doc = Doc::text("having"); - if let Some(expr) = having.expr() { +fn build_drop_group<'a>(stmt: &ast::DropGroup) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.group_token(), "group")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(expr.syntax())) - .append(build_expr(expr)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc -} - -fn build_window_clause<'a>(window: ast::WindowClause) -> Doc<'a> { - let defs = window.window_defs().map(|def| { + if let Some(groups) = build_comma_separated_docs(stmt.role_refs().map(|group| { + let syntax = group.syntax().clone(); ( - leading_comments(def.syntax()).append(build_window_def(def.clone())), - def.syntax().clone(), + leading_comments(&syntax).append(build_role_ref(&group)), + syntax, ) - }); - let mut doc = Doc::text("window"); - if let Some(defs) = build_comma_separated_docs(defs) { - doc = doc.append(Doc::space()).append(defs.nest(2)); + })) { + doc = doc.append(Doc::line_or_space().append(groups).nest(2)); } - doc.group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_window_def<'a>(def: ast::WindowDef) -> Doc<'a> { - let mut doc = def - .window() - .map(|window| build_name(window.syntax())) - .unwrap_or_else(Doc::nil); - if let Some(as_token) = def.as_token() { +fn build_drop_index<'a>(stmt: &ast::DropIndex) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.index_token(), "index"), + (stmt.concurrently_token(), "concurrently"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&as_token)) - .append(Doc::text("as")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(l_paren) = def.l_paren_token() { + if let Some(indexes) = build_comma_separated_docs(stmt.index_refs().map(|index| { + let syntax = index.syntax().clone(); + (build_extension_path_value(&index), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(indexes).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_language<'a>(stmt: &ast::DropLanguage) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.procedural_token(), "procedural"), + (stmt.language_token(), "language"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&l_paren)) - .append(Doc::text("(")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - let mut body = def - .window_spec() - .map(|spec| leading_comments(spec.syntax()).append(build_window_spec(spec))) - .unwrap_or_else(Doc::nil); - if let Some(r_paren) = def.r_paren_token() { - body = body.append(comments_before(r_paren)); + if let Some(language) = stmt.language_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(language.syntax())) + .append(build_name(language.syntax())) + .nest(2), + ); } - doc.append(wrap_body(body)).append(Doc::text(")")).group() + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_into_clause<'a>(into: ast::IntoClause) -> Doc<'a> { - let mut doc = Doc::text("into"); - if let Some(persistence) = into.persistence() { +fn build_drop_materialized_view<'a>(stmt: &ast::DropMaterializedView) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.materialized_token(), "materialized"), + (stmt.view_token(), "view"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(persistence.syntax())) - .append(build_keyword_node(persistence.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(table_token) = into.table_token() { + if let Some(views) = build_comma_separated_docs(stmt.view_refs().map(|view| { + let syntax = view.syntax().clone(); + (build_extension_path_value(&view), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(views).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_operator<'a>(stmt: &ast::DropOperator) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.operator_token(), "operator"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&table_token)) - .append(Doc::text("table")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(table_name) = into.table_name() { + if let Some(list) = stmt.op_sig_list() { + if let Some(operators) = build_comma_separated_docs(list.op_sigs().map(|operator| { + let syntax = operator.syntax().clone(); + ( + leading_comments(&syntax).append(build_op_sig(operator)), + syntax, + ) + })) { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(operators) + .nest(2), + ); + } + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_operator_class<'a>(stmt: &ast::DropOperatorClass) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.operator_token(), "operator"), + (stmt.class_token(), "class"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(table_name.syntax())); - if let Some(path) = table_name.path() { - doc = doc.append(build_path(&path)); - } + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(class) = stmt.op_class_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&class)) + .nest(2), + ); } - doc + if let Some(using) = stmt.using_method() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_select_group_by_clause<'a>(group: ast::GroupByClause) -> Doc<'a> { - let mut doc = Doc::text("group").append(Doc::space()); - if let Some(by_token) = group.by_token() { - doc = doc.append(leading_comments_token(&by_token)); - } - doc = doc.append(Doc::text("by")).append(Doc::space()); - if let Some(quantifier) = group.all_or_distinct() { +fn build_drop_operator_family<'a>(stmt: &ast::DropOperatorFamily) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.operator_token(), "operator"), + (stmt.family_token(), "family"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc - .append(leading_comments(quantifier.syntax())) - .append(match quantifier { - ast::AllOrDistinct::All(_) => Doc::text("all"), - ast::AllOrDistinct::Distinct(_) => Doc::text("distinct"), - }) - .append(Doc::space()); + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(list) = group.group_by_list() { - doc = doc.append(build_group_by_list(list)); + if let Some(family) = stmt.op_family_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&family)) + .nest(2), + ); } - doc + if let Some(using) = stmt.using_method() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(using.syntax())) + .append(build_using_method(using)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_create_publication<'a>(stmt: &ast::CreatePublication) -> Doc<'a> { - let mut doc = Doc::text("create"); - if let Some(token) = stmt.publication_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("publication")); +fn build_drop_owned<'a>(stmt: &ast::DropOwned) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.owned_token(), "owned"), + (stmt.by_token(), "by"), + ]); + if let Some(roles) = stmt.role_ref_list() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(roles.syntax())) + .append(build_role_ref_list(roles)) + .nest(2), + ); } - if let Some(publication) = stmt.publication() { + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_policy<'a>(stmt: &ast::DropPolicy) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.policy_token(), "policy")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(publication.syntax())) - .append(build_name(publication.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(clause) = stmt.publication_for_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(clause.syntax())) - .append(build_publication_for_clause(clause)); + if let Some(policy) = stmt.policy_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(policy.syntax())) + .append(build_name(policy.syntax())) + .nest(2), + ); } - if let Some(params) = stmt.with_params() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(params.syntax())) - .append(build_with_params(params)); + if let Some(on) = stmt.on_table() { + let mut on_doc = build_keyword_tokens([(on.on_token(), "on")]); + if let Some(table) = on.table_name_ref() { + on_doc = on_doc + .append(Doc::space()) + .append(build_extension_path_value(&table)); + } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); } - doc.append(build_semicolon(stmt.semicolon_token())).group() + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_publication_for_clause<'a>(clause: ast::PublicationForClause) -> Doc<'a> { - match clause { - ast::PublicationForClause::ForAllPublicationObjects(clause) => { - let mut doc = Doc::text("for"); - if let Some(objects) = build_all_publication_objects(clause.all_publication_objects()) { - doc = doc.append(Doc::line_or_space().append(objects).nest(2)); - } - if let Some(except) = clause.except_table_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(except.syntax())) - .append(build_except_table_clause(except)); - } - doc.group() - } - ast::PublicationForClause::ForPublicationObjects(clause) => { - let mut doc = Doc::text("for"); - if let Some(objects) = build_publication_objects(clause.publication_objects()) { - doc = doc.append(Doc::line_or_space().append(objects).nest(2)); - } - doc.group() +fn build_drop_procedure<'a>(stmt: &ast::DropProcedure) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.procedure_token(), "procedure"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(list) = stmt.procedure_sig_list() { + if let Some(procedures) = + build_comma_separated_docs(list.procedure_sigs().map(|procedure| { + let syntax = procedure.syntax().clone(); + ( + leading_comments(&syntax).append(build_procedure_sig(procedure)), + syntax, + ) + })) + { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(procedures) + .nest(2), + ); } } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_all_publication_objects<'a>( - objects: impl Iterator, -) -> Option> { - build_comma_separated_docs(objects.map(|object| { - let syntax = object.syntax().clone(); - let doc = leading_comments(object.syntax()).append(match object { - ast::AllPublicationObject::AllPublicationTables(object) => { - let mut doc = Doc::text("all"); - if let Some(token) = object.tables_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("tables")); - } - doc - } - ast::AllPublicationObject::AllPublicationSequences(object) => { - let mut doc = Doc::text("all"); - if let Some(token) = object.sequences_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("sequences")); - } - doc - } - }); - (doc, syntax) - })) -} - -fn build_publication_objects<'a>( - objects: impl Iterator, -) -> Option> { - build_comma_separated_docs(objects.map(|object| { - let syntax = object.syntax().clone(); - ( - leading_comments(object.syntax()).append(build_publication_object(object)), - syntax, - ) - })) +fn build_drop_property_graph<'a>(stmt: &ast::DropPropertyGraph) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.property_token(), "property"), + (stmt.graph_token(), "graph"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(graph) = stmt.property_graph_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&graph)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_publication_object<'a>(object: ast::PublicationObject) -> Doc<'a> { - match object { - ast::PublicationObject::PublicationObjectCurrentSchema(_) => Doc::text("current_schema"), - ast::PublicationObject::PublicationObjectTable(object) => { - let mut doc = Doc::text("table"); - if let Some(token) = object.only_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("only")); - } - let parenthesized = object.l_paren_token().is_some(); - if let Some(l_paren) = object.l_paren_token() { - doc = doc - .append(Doc::space()) - .append(comments_before(l_paren)) - .append(Doc::text("(")); - } - if let Some(table) = object.table_name_ref() { - if !parenthesized { - doc = doc.append(Doc::space()); - } - doc = doc.append(leading_comments(table.syntax())); - if let Some(path) = table.path_ref() { - doc = doc.append(build_path_ref(&path)); - } - } - if let Some(r_paren) = object.r_paren_token() { - doc = doc.append(comments_before(r_paren)).append(Doc::text(")")); - } - if let Some(star) = object.star_token() { - doc = doc - .append(leading_comments_token(&star)) - .append(Doc::text("*")); - } - if let Some(columns) = object.column_ref_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(columns.syntax())) - .append(build_column_ref_list(columns)); - } - if let Some(where_clause) = object.where_condition_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(where_clause.syntax())) - .append(build_where_condition_clause(where_clause)); - } - doc.group() - } - ast::PublicationObject::PublicationObjectTablesInSchema(object) => { - let mut doc = Doc::text("tables"); - if let Some(token) = object.in_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("in")); - } - if let Some(token) = object.schema_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("schema")); - } - if let Some(token) = object.current_schema_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("current_schema")); - } else if let Some(schema) = object.schema_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(schema.syntax())) - .append(build_name(schema.syntax())); - } - if let Some(where_clause) = object.where_condition_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(where_clause.syntax())) - .append(build_where_condition_clause(where_clause)); - } - doc.group() - } +fn build_drop_role<'a>(stmt: &ast::DropRole) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.role_token(), "role")]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(roles) = build_comma_separated_docs(stmt.role_refs().map(|role| { + let syntax = role.syntax().clone(); + ( + leading_comments(&syntax).append(build_role_ref(&role)), + syntax, + ) + })) { + doc = doc.append(Doc::line_or_space().append(roles).nest(2)); } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_except_table_clause<'a>(clause: ast::ExceptTableClause) -> Doc<'a> { - let mut doc = Doc::text("except"); - if let Some(l_paren) = clause.l_paren_token() { +fn build_drop_routine<'a>(stmt: &ast::DropRoutine) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.routine_token(), "routine"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(comments_before(l_paren)) - .append(Doc::text("(")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - let items = clause.except_table_names().map(|name| { - let mut item = Doc::nil(); - if let Some(table_token) = name.table_token() { - item = item - .append(leading_comments_token(&table_token)) - .append(Doc::text("table")) - .append(Doc::space()); + if let Some(list) = stmt.routine_sig_list() { + if let Some(routines) = build_comma_separated_docs(list.routine_sigs().map(|routine| { + let syntax = routine.syntax().clone(); + ( + leading_comments(&syntax).append(build_routine_sig(routine)), + syntax, + ) + })) { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(list.syntax())) + .append(routines) + .nest(2), + ); } - if let Some(table) = name.table_relation_name() { - item = item - .append(leading_comments(table.syntax())) - .append(build_table_relation_name(table)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_rule<'a>(stmt: &ast::DropRule) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.rule_token(), "rule")]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(rule) = stmt.rule_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(rule.syntax())) + .append(build_name(rule.syntax())) + .nest(2), + ); + } + if let Some(on) = stmt.on_relation() { + let mut on_doc = build_keyword_tokens([(on.on_token(), "on")]); + if let Some(relation) = on.relation_name_ref() { + on_doc = on_doc + .append(Doc::space()) + .append(build_extension_path_value(&relation)); } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_schema<'a>(stmt: &ast::DropSchema) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.schema_token(), "schema")]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(schemas) = build_comma_separated_docs(stmt.schema_refs().map(|schema| { + let syntax = schema.syntax().clone(); ( - leading_comments(name.syntax()).append(item), - name.syntax().clone(), + leading_comments(&syntax).append(build_name(&syntax)), + syntax, ) - }); - let mut body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - if let Some(r_paren) = clause.r_paren_token() { - body = body.append(comments_before(r_paren)); + })) { + doc = doc.append(Doc::line_or_space().append(schemas).nest(2)); } - doc.append(wrap_body(body)).append(Doc::text(")")).group() + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_alter_publication<'a>(stmt: &ast::AlterPublication) -> Doc<'a> { - let mut doc = Doc::text("alter"); - if let Some(token) = stmt.publication_token() { +fn build_drop_sequence<'a>(stmt: &ast::DropSequence) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.sequence_token(), "sequence"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("publication")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(publication) = stmt.publication_ref() { + if let Some(sequences) = build_comma_separated_docs(stmt.sequence_refs().map(|sequence| { + let syntax = sequence.syntax().clone(); + (build_extension_path_value(&sequence), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(sequences).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_server<'a>(stmt: &ast::DropServer) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.server_token(), "server")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(publication.syntax())) - .append(build_name(publication.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(action) = stmt.action() { + if let Some(servers) = build_comma_separated_docs(stmt.server_refs().map(|server| { + let syntax = server.syntax().clone(); + ( + leading_comments(&syntax).append(build_name(&syntax)), + syntax, + ) + })) { + doc = doc.append(Doc::line_or_space().append(servers).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_statistics<'a>(stmt: &ast::DropStatistics) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.statistics_token(), "statistics"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(action.syntax())) - .append(build_alter_publication_action(action)); + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc.append(build_semicolon(stmt.semicolon_token())).group() + if let Some(statistics) = build_comma_separated_docs(stmt.statistics_refs().map(|statistics| { + let syntax = statistics.syntax().clone(); + (build_extension_path_value(&statistics), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(statistics).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_alter_publication_action<'a>(action: ast::AlterPublicationAction) -> Doc<'a> { - match action { - ast::AlterPublicationAction::AddPublicationObjects(action) => { - build_publication_object_action("add", action.publication_objects()) - } - ast::AlterPublicationAction::DropPublicationObjects(action) => { - build_publication_object_action("drop", action.publication_objects()) - } - ast::AlterPublicationAction::SetPublicationObjects(action) => { - build_publication_object_action("set", action.publication_objects()) - } - ast::AlterPublicationAction::SetAllPublicationObjectList(action) => { - let mut doc = Doc::text("set"); - if let Some(objects) = build_all_publication_objects(action.all_publication_objects()) { - doc = doc.append(Doc::line_or_space().append(objects).nest(2)); - } - if let Some(except) = action.except_table_clause() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(except.syntax())) - .append(build_except_table_clause(except)); - } - doc.group() - } - ast::AlterPublicationAction::SetOptions(action) => build_set_options(action), - ast::AlterPublicationAction::OwnerTo(action) => build_owner_to(action), - ast::AlterPublicationAction::PublicationRenameTo(action) => { - let mut doc = Doc::text("rename"); - if let Some(token) = action.to_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("to")); - } - if let Some(publication) = action.publication() { - doc = doc - .append(Doc::space()) - .append(leading_comments(publication.syntax())) - .append(build_name(publication.syntax())); - } - doc - } +fn build_drop_table<'a>(stmt: &ast::DropTable) -> Doc<'a> { + let mut doc = + build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.table_token(), "table")]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } + if let Some(tables) = build_comma_separated_docs(stmt.table_name_refs().map(|table| { + let syntax = table.syntax().clone(); + (build_extension_path_value(&table), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(tables).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_publication_object_action<'a>( - keyword: &'static str, - objects: impl Iterator, -) -> Doc<'a> { - let mut doc = Doc::text(keyword); - if let Some(objects) = build_publication_objects(objects) { - doc = doc.append(Doc::line_or_space().append(objects).nest(2)); +fn build_drop_view<'a>(stmt: &ast::DropView) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.view_token(), "view")]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc.group() + if let Some(views) = build_comma_separated_docs(stmt.view_refs().map(|view| { + let syntax = view.syntax().clone(); + (build_extension_path_value(&view), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(views).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_set_options<'a>(options: ast::SetOptions) -> Doc<'a> { - let mut doc = Doc::text("set"); - if let Some(attributes) = options.attribute_list() { +fn build_drop_tablespace<'a>(stmt: &ast::DropTablespace) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.tablespace_token(), "tablespace"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(attributes.syntax())) - .append(build_attribute_list(attributes)); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc + if let Some(tablespace) = stmt.tablespace_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(tablespace.syntax())) + .append(build_name(tablespace.syntax())) + .nest(2), + ); + } + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_owner_to<'a>(owner: ast::OwnerTo) -> Doc<'a> { - let mut doc = Doc::text("owner"); - if let Some(token) = owner.to_token() { +fn build_drop_text_search_config<'a>(stmt: &ast::DropTextSearchConfig) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.configuration_token(), "configuration"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("to")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(role) = owner.role_ref() { + if let Some(configuration) = stmt.text_search_configuration_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&configuration)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_text_search_dict<'a>(stmt: &ast::DropTextSearchDict) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.dictionary_token(), "dictionary"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(role.syntax())) - .append(build_name(role.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - doc + if let Some(dictionary) = stmt.text_search_dictionary_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&dictionary)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_create_subscription<'a>(stmt: &ast::CreateSubscription) -> Doc<'a> { - let mut doc = Doc::text("create"); - if let Some(token) = stmt.subscription_token() { +fn build_drop_text_search_parser<'a>(stmt: &ast::DropTextSearchParser) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.parser_token(), "parser"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("subscription")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(subscription) = stmt.subscription() { + if let Some(parser) = stmt.text_search_parser_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&parser)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_text_search_template<'a>(stmt: &ast::DropTextSearchTemplate) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.text_token(), "text"), + (stmt.search_token(), "search"), + (stmt.template_token(), "template"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(subscription.syntax())) - .append(build_name(subscription.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(source) = stmt.source() { + if let Some(template) = stmt.text_search_template_ref() { + doc = doc.append( + Doc::line_or_space() + .append(build_extension_path_value(&template)) + .nest(2), + ); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_transform<'a>(stmt: &ast::DropTransform) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.transform_token(), "transform"), + ]); + if let Some(if_exists) = stmt.if_exists() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(source.syntax())) - .append(build_subscription_source(source)); + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(token) = stmt.publication_token() { + if let Some(for_token) = stmt.for_token() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments_token(&token)) - .append(Doc::text("publication")); + .append(Doc::space()) + .append(leading_comments_token(&for_token)) + .append(Doc::text("for")); } - if let Some(publications) = build_publication_refs(stmt.publication_refs()) { - doc = doc.append(Doc::space()).append(publications.nest(2)); + if let Some(ty) = stmt.ty() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(ty.syntax())) + .append(build_type(ty)) + .nest(2), + ); } - if let Some(params) = stmt.with_params() { + if let Some(language_token) = stmt.language_token() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments_token(&language_token)) + .append(Doc::text("language")) + .nest(2), + ); + } + if let Some(language) = stmt.language_ref() { doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(params.syntax())) - .append(build_with_params(params)); + .append(Doc::space()) + .append(leading_comments(language.syntax())) + .append(build_name(language.syntax())); } - doc.append(build_semicolon(stmt.semicolon_token())).group() + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_subscription_source<'a>(source: ast::SubscriptionSource) -> Doc<'a> { - match source { - ast::SubscriptionSource::ConnectionClause(source) => { - let mut doc = Doc::text("connection"); - if let Some(literal) = source.literal() { - doc = doc - .append(Doc::space()) - .append(leading_comments(literal.syntax())) - .append(build_literal(literal)); - } - doc - } - ast::SubscriptionSource::ServerClause(source) => { - let mut doc = Doc::text("server"); - if let Some(server) = source.server_ref() { - doc = doc - .append(Doc::space()) - .append(leading_comments(server.syntax())) - .append(build_name(server.syntax())); - } - doc +fn build_drop_trigger<'a>(stmt: &ast::DropTrigger) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.trigger_token(), "trigger"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); + } + if let Some(trigger) = stmt.trigger_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(trigger.syntax())) + .append(build_name(trigger.syntax())) + .nest(2), + ); + } + if let Some(on) = stmt.on_relation() { + let mut on_doc = build_keyword_tokens([(on.on_token(), "on")]); + if let Some(relation) = on.relation_name_ref() { + on_doc = on_doc + .append(Doc::space()) + .append(build_extension_path_value(&relation)); } + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(on.syntax())) + .append(on_doc) + .nest(2), + ); } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_publication_refs<'a>( - publications: impl Iterator, -) -> Option> { - build_comma_separated_docs(publications.map(|publication| { - ( - leading_comments(publication.syntax()).append(build_name(publication.syntax())), - publication.syntax().clone(), - ) - })) -} - -fn build_alter_subscription<'a>(stmt: &ast::AlterSubscription) -> Doc<'a> { - let mut doc = Doc::text("alter"); - if let Some(token) = stmt.subscription_token() { +fn build_drop_type<'a>(stmt: &ast::DropType) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.type_token(), "type")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("subscription")); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(subscription) = stmt.subscription_ref() { + if let Some(types) = build_comma_separated_docs(stmt.type_name_refs().map(|ty| { + let syntax = ty.syntax().clone(); + (build_extension_path_value(&ty), syntax) + })) { + doc = doc.append(Doc::line_or_space().append(types).nest(2)); + } + doc = append_drop_behavior(doc, stmt.drop_behavior()); + doc.group().append(build_semicolon(stmt.semicolon_token())) +} + +fn build_drop_user<'a>(stmt: &ast::DropUser) -> Doc<'a> { + let mut doc = build_keyword_tokens([(stmt.drop_token(), "drop"), (stmt.user_token(), "user")]); + if let Some(if_exists) = stmt.if_exists() { doc = doc .append(Doc::space()) - .append(leading_comments(subscription.syntax())) - .append(build_name(subscription.syntax())); + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } - if let Some(action) = stmt.action() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(action.syntax())) - .append(build_alter_subscription_action(action)); + if let Some(users) = build_comma_separated_docs(stmt.role_refs().map(|user| { + let syntax = user.syntax().clone(); + ( + leading_comments(&syntax).append(build_role_ref(&user)), + syntax, + ) + })) { + doc = doc.append(Doc::line_or_space().append(users).nest(2)); } - doc.append(build_semicolon(stmt.semicolon_token())).group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } -fn build_alter_subscription_action<'a>(action: ast::AlterSubscriptionAction) -> Doc<'a> { - match action { - ast::AlterSubscriptionAction::ConnectionClause(action) => { - build_subscription_source(ast::SubscriptionSource::ConnectionClause(action)) - } - ast::AlterSubscriptionAction::ServerClause(action) => { - build_subscription_source(ast::SubscriptionSource::ServerClause(action)) - } - ast::AlterSubscriptionAction::SetOptions(action) => build_set_options(action), - ast::AlterSubscriptionAction::AddPublication(action) => { - build_subscription_publication_action( - "add", - action.publication_token(), - action.publication_refs(), - action.with_params(), - ) - } - ast::AlterSubscriptionAction::SetPublication(action) => { - build_subscription_publication_action( - "set", - action.publication_token(), - action.publication_refs(), - action.with_params(), - ) - } - ast::AlterSubscriptionAction::DropSubscriptionPublication(action) => { - build_subscription_publication_action( - "drop", - action.publication_token(), - action.publication_refs(), - action.with_params(), - ) - } - ast::AlterSubscriptionAction::RefreshPublication(action) => { - let mut doc = Doc::text("refresh").append(Doc::space()); - if let Some(token) = action.publication_token() { - doc = doc - .append(leading_comments_token(&token)) - .append(Doc::text("publication")); - } - if let Some(params) = action.with_params() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(params.syntax())) - .append(build_with_params(params)); - } - doc.group() - } - ast::AlterSubscriptionAction::EnableSubscription(_) => Doc::text("enable"), - ast::AlterSubscriptionAction::DisableSubscription(_) => Doc::text("disable"), - ast::AlterSubscriptionAction::SkipSubscription(action) => { - let mut doc = Doc::text("skip"); - if let Some(attributes) = action.attribute_list() { - doc = doc - .append(Doc::space()) - .append(leading_comments(attributes.syntax())) - .append(build_attribute_list(attributes)); - } - doc - } - ast::AlterSubscriptionAction::OwnerTo(action) => build_owner_to(action), - ast::AlterSubscriptionAction::SubscriptionRenameTo(action) => { - let mut doc = Doc::text("rename"); - if let Some(token) = action.to_token() { - doc = doc - .append(Doc::space()) - .append(leading_comments_token(&token)) - .append(Doc::text("to")); - } - if let Some(subscription) = action.subscription() { - doc = doc - .append(Doc::space()) - .append(leading_comments(subscription.syntax())) - .append(build_name(subscription.syntax())); - } - doc - } +fn build_drop_user_mapping<'a>(stmt: &ast::DropUserMapping) -> Doc<'a> { + let mut doc = build_keyword_tokens([ + (stmt.drop_token(), "drop"), + (stmt.user_token(), "user"), + (stmt.mapping_token(), "mapping"), + ]); + if let Some(if_exists) = stmt.if_exists() { + doc = doc + .append(Doc::space()) + .append(leading_comments(if_exists.syntax())) + .append(build_if_exists(if_exists)); } -} - -fn build_subscription_publication_action<'a>( - keyword: &'static str, - publication_token: Option, - publications: impl Iterator, - params: Option, -) -> Doc<'a> { - let mut doc = Doc::text(keyword).append(Doc::space()); - if let Some(token) = publication_token { + if let Some(for_token) = stmt.for_token() { doc = doc - .append(leading_comments_token(&token)) - .append(Doc::text("publication")); + .append(Doc::space()) + .append(leading_comments_token(&for_token)) + .append(Doc::text("for")); } - if let Some(publications) = build_publication_refs(publications) { - doc = doc.append(Doc::space()).append(publications.nest(2)); + if let Some(role) = stmt.role_ref() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(role.syntax())) + .append(build_role_ref(&role)) + .nest(2), + ); } - if let Some(params) = params { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(params.syntax())) - .append(build_with_params(params)); + if let Some(server) = stmt.server_clause() { + doc = doc.append( + Doc::line_or_space() + .append(leading_comments(server.syntax())) + .append(build_server_clause(server)) + .nest(2), + ); } - doc.group() + doc.group().append(build_semicolon(stmt.semicolon_token())) } fn build_drop_publication<'a>(stmt: &ast::DropPublication) -> Doc<'a> { @@ -8848,12 +17636,7 @@ fn build_drop_publication<'a>(stmt: &ast::DropPublication) -> Doc<'a> { .append(Doc::line_or_space()) .append(publications.nest(2)); } - if let Some(behavior) = stmt.drop_behavior() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(behavior.syntax())) - .append(build_drop_behavior(behavior)); - } + doc = append_drop_behavior(doc, stmt.drop_behavior()); doc.append(build_semicolon(stmt.semicolon_token())).group() } @@ -8877,12 +17660,7 @@ fn build_drop_subscription<'a>(stmt: &ast::DropSubscription) -> Doc<'a> { .append(leading_comments(subscription.syntax())) .append(build_name(subscription.syntax())); } - if let Some(behavior) = stmt.drop_behavior() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(behavior.syntax())) - .append(build_drop_behavior(behavior)); - } + doc = append_drop_behavior(doc, stmt.drop_behavior()); doc.append(build_semicolon(stmt.semicolon_token())).group() } @@ -8897,6 +17675,19 @@ fn build_if_exists<'a>(if_exists: ast::IfExists) -> Doc<'a> { doc } +fn append_drop_behavior<'a>(doc: Doc<'a>, behavior: Option) -> Doc<'a> { + if let Some(behavior) = behavior { + doc.append( + Doc::line_or_space() + .append(leading_comments(behavior.syntax())) + .append(build_drop_behavior(behavior)) + .nest(2), + ) + } else { + doc + } +} + fn build_drop_behavior<'a>(behavior: ast::DropBehavior) -> Doc<'a> { match behavior { ast::DropBehavior::Cascade(_) => Doc::text("cascade"), @@ -12671,46 +21462,46 @@ fn build_parenthesized_expr_or_select_fn<'a>( } fn build_call_arg_list<'a>(arg_list: ast::ArgList) -> Doc<'a> { + let has_body = arg_list.star_token().is_some() + || arg_list.all_or_distinct().is_some() + || arg_list.args().next().is_some() + || arg_list + .r_paren_token() + .is_some_and(|r_paren| !comment_tokens_before(r_paren).is_empty()); let mut doc = Doc::nil(); if let Some(l_paren) = arg_list.l_paren_token() { doc = doc.append(comments_before(l_paren)); } doc = doc.append(Doc::text("(")); - let mut body = Doc::nil(); - if let Some(star) = arg_list.star_token() { - body = body - .append(leading_comments_token(&star)) - .append(Doc::text("*")); + let mut body = if let Some(star) = arg_list.star_token() { + leading_comments_token(&star).append(Doc::text("*")) } else { - let mut has_quantifier = false; - if let Some(quantifier) = arg_list.all_or_distinct() { - has_quantifier = true; - body = body - .append(leading_comments(quantifier.syntax())) - .append(match quantifier { - ast::AllOrDistinct::All(_) => Doc::text("all"), - ast::AllOrDistinct::Distinct(_) => Doc::text("distinct"), - }); - } - - let args = arg_list.args().map(|arg| { + let quantifier = arg_list.all_or_distinct().map(|quantifier| { + leading_comments(quantifier.syntax()).append(match quantifier { + ast::AllOrDistinct::All(_) => Doc::text("all"), + ast::AllOrDistinct::Distinct(_) => Doc::text("distinct"), + }) + }); + let args = build_comma_separated_docs(arg_list.args().map(|arg| { let syntax = arg.syntax().clone(); let doc = leading_comments(arg.syntax()).append(build_call_arg(arg)); (doc, syntax) - }); - if let Some(args) = build_comma_separated_docs(args) { - if has_quantifier { - body = body.append(Doc::space()); - } - body = body.append(args); + })); + match (quantifier, args) { + (Some(quantifier), Some(args)) => quantifier.append(Doc::space()).append(args), + (Some(quantifier), None) => quantifier, + (None, Some(args)) => args, + (None, None) => Doc::nil(), } - } + }; if let Some(r_paren) = arg_list.r_paren_token() { body = body.append(comments_before(r_paren)); } - doc = doc.append(wrap_body(body)); + if has_body { + doc = doc.append(wrap_body(body)); + } doc.append(Doc::text(")")).group() } @@ -14035,6 +22826,20 @@ fn build_interval_qualifier<'a>(qualifier: &ast::IntervalQualifier) -> Doc<'a> { } } +fn space_before_l_paren<'a>(l_paren: SyntaxToken) -> Doc<'a> { + let comments = comment_tokens_before(l_paren.clone()); + if comments.is_empty() { + return Doc::space(); + } + let ends_with_line_comment = comments.last().is_some_and(is_line_comment); + let doc = comments_before(l_paren); + if ends_with_line_comment { + doc + } else { + doc.append(Doc::space()) + } +} + fn comments_before<'a>(el: impl Into) -> Doc<'a> { let mut doc = Doc::nil(); for token in comment_tokens_before(el) { diff --git a/crates/squawk_fmt/tests/after/alter_aggregate.snap b/crates/squawk_fmt/tests/after/alter_aggregate.snap new file mode 100644 index 00000000..090cb69a --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_aggregate.snap @@ -0,0 +1,22 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_aggregate.sql +--- +alter aggregate public.total_sales(numeric) rename to total_revenue; + +alter aggregate public.total_sales(numeric) owner to reporting_role; + +alter aggregate public.total_sales(numeric) set schema reporting; + +alter aggregate extraordinarily_long_schema_name.extraordinarily_long_aggregate_name( + double precision, + numeric, + bigint +) + rename to another_extraordinarily_long_aggregate_name; + +alter /* aggregate keyword */ aggregate /* aggregate signature */ public.commented_total( + /* first parameter */ numeric, + /* second parameter */ bigint +) + /* action */ rename /* to keyword */ to /* new name */ commented_revenue/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_collation.snap b/crates/squawk_fmt/tests/after/alter_collation.snap new file mode 100644 index 00000000..50f12107 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_collation.snap @@ -0,0 +1,20 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_collation.sql +--- +alter collation public.en_us rename to en_us_utf8; + +alter collation public.en_us owner to localization_admin; + +alter collation public.en_us refresh version; + +alter collation public.en_us set schema localization; + +alter collation extraordinarily_long_schema_name.extraordinarily_long_collation_name + rename to another_extraordinarily_long_collation_name; + +alter /* collation keyword */ collation /* collation name */ public.commented_en_us + /* action */ refresh /* version keyword */ version/* semicolon */; + +alter collation /* name */ public.comment_positions + /* action */ set /* schema keyword */ schema /* schema name */ commented_localization; diff --git a/crates/squawk_fmt/tests/after/alter_conversion.snap b/crates/squawk_fmt/tests/after/alter_conversion.snap new file mode 100644 index 00000000..2d37fbe8 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_conversion.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_conversion.sql +--- +alter conversion public.latin1_to_utf8 rename to latin_to_utf8; + +alter conversion public.latin1_to_utf8 owner to encoding_admin; + +alter conversion public.latin1_to_utf8 set schema encodings; + +alter conversion extraordinarily_long_schema_name.extraordinarily_long_conversion_name + rename to another_extraordinarily_long_conversion_name; + +alter /* conversion keyword */ conversion /* conversion name */ public.commented_conversion + /* action */ rename /* to keyword */ to /* new name */ renamed_conversion/* semicolon */; + +alter conversion /* conversion name */ public.schema_comments + /* action */ set /* schema keyword */ schema /* schema name */ archived_encodings; diff --git a/crates/squawk_fmt/tests/after/alter_database.snap b/crates/squawk_fmt/tests/after/alter_database.snap new file mode 100644 index 00000000..d6bfbea2 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_database.snap @@ -0,0 +1,47 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_database.sql +--- +alter database app rename to application; + +alter database app owner to app_admin; + +alter database app refresh collation version; + +alter database app reset all; + +alter database app reset statement_timeout; + +alter database app set statement_timeout to '10s'; + +alter database app set tablespace fast_storage; + +alter database app + with + connection limit = 100 + encoding = 'UTF8' + lc_collate = default + owner = app_admin + tablespace = fast_storage + template = template0; + +alter database extraordinarily_long_database_name + with + connection limit = 1000 + encoding = 'UTF8' + owner = extraordinarily_long_database_owner + tablespace = extraordinarily_long_tablespace_name; + +alter /* database keyword */ database /* database name */ commented_app + /* action */ with + /* option */ connection /* limit keyword */ limit /* equals */ = /* value */ 25 + /* next option */ encoding /* equals */ = /* value */ 'UTF8' + /* owner option */ owner /* equals */ = /* owner value */ app_admin + /* tablespace option */ tablespace /* equals */ = /* tablespace value */ fast_storage + /* template option */ template /* equals */ = /* template value */ template0/* semicolon */; + +alter database /* name */ commented_config + /* action */ set /* parameter */ statement_timeout /* assignment */ to /* value */ '30s'; + +alter database /* name */ commented_refresh + /* action */ refresh /* collation keyword */ collation /* version keyword */ version/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_default_privileges.snap b/crates/squawk_fmt/tests/after/alter_default_privileges.snap new file mode 100644 index 00000000..b7b749ed --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_default_privileges.snap @@ -0,0 +1,45 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_default_privileges.sql +--- +alter default privileges grant select on tables to reporting; + +alter default privileges + for role + app_owner, + migrations + in schema + public, + audit + grant all privileges on sequences to app_user, reporting with grant option; + +alter default privileges + for user + app_owner + revoke grant option for insert, update on tables from app_user cascade; + +alter default privileges grant execute on functions to public; + +alter default privileges grant execute on routines to public; + +alter default privileges grant usage on schemas to public; + +alter default privileges grant usage on types to public; + +alter default privileges grant select on large objects to public; + +alter /* default keyword */ default /* privileges keyword */ privileges + /* for clause */ for /* role keyword */ role + /* first role */ exceedingly_long_application_owner, + /* second role */ exceedingly_long_migration_owner + /* schemas clause */ in /* schema keyword */ schema + /* first schema */ exceedingly_long_application_schema, + /* second schema */ exceedingly_long_audit_schema + /* action */ grant + /* privilege */ select, + /* another privilege */ update + /* on keyword */ on + /* target */ tables + /* to keyword */ to + /* grantee */ exceedingly_long_reporting_role + /* with clause */ with /* grant keyword */ grant /* option keyword */ option/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_domain.snap b/crates/squawk_fmt/tests/after/alter_domain.snap new file mode 100644 index 00000000..6705b511 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_domain.snap @@ -0,0 +1,35 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_domain.sql +--- +alter domain public.email_address set default 'unknown@example.com'; + +alter domain public.email_address drop default; + +alter domain public.email_address set not null; + +alter domain public.email_address drop not null; + +alter domain public.email_address + add constraint email_format check (value ~ '@'); + +alter domain public.email_address + drop constraint if exists old_email_format cascade; + +alter domain public.email_address + rename constraint email_format to valid_email_format; + +alter domain public.email_address validate constraint valid_email_format; + +alter domain public.email_address owner to app_owner; + +alter domain public.email_address rename to contact_email_address; + +alter domain public.email_address set schema application_types; + +alter /* domain */ domain /* name */ exceedingly_long_schema_name.exceedingly_long_domain_name + /* action */ add + /* constraint keyword */ constraint /* constraint name */ exceedingly_long_constraint_name /* check */ check /* expression */ ( + value is not null and length(value) > 10 + ) + /* not valid */ not /* valid */ valid/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_event_trigger.snap b/crates/squawk_fmt/tests/after/alter_event_trigger.snap new file mode 100644 index 00000000..3c3ab59c --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_event_trigger.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_event_trigger.sql +--- +alter event trigger ddl_audit enable; + +alter event trigger ddl_audit disable; + +alter event trigger ddl_audit enable always; + +alter event trigger ddl_audit enable replica; + +alter event trigger ddl_audit owner to audit_owner; + +alter event trigger ddl_audit rename to renamed_ddl_audit; + +alter /* event */ event /* trigger */ trigger /* name */ an_exceedingly_long_event_trigger_name_used_for_auditing + /* action */ enable /* always */ always/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_extension.snap b/crates/squawk_fmt/tests/after/alter_extension.snap new file mode 100644 index 00000000..fd9a45d1 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_extension.snap @@ -0,0 +1,90 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_extension.sql +--- +alter extension hstore update; + +alter extension hstore update to '1.8'; + +alter extension hstore set schema extensions; + +alter extension hstore add access method custom_heap; + +alter extension hstore add aggregate public.total(integer); + +alter extension hstore add cast (integer as text); + +alter extension hstore add collation public.english; + +alter extension hstore add conversion public.latin_conversion; + +alter extension hstore add database app_db; + +alter extension hstore add domain public.email_address; + +alter extension hstore add event trigger ddl_audit; + +alter extension hstore add extension dependent_extension; + +alter extension hstore add foreign data wrapper app_fdw; + +alter extension hstore add foreign table public.remote_accounts; + +alter extension hstore add function public.calculate(integer, text); + +alter extension hstore add index public.accounts_email_idx; + +alter extension hstore add procedural language plpgsql; + +alter extension hstore add materialized view public.account_totals; + +alter extension hstore add operator public.=== (integer, integer); + +alter extension hstore add operator class public.custom_ops using btree; + +alter extension hstore add operator family public.custom_family using btree; + +alter extension hstore add procedure public.refresh_accounts(integer); + +alter extension hstore add publication account_changes; + +alter extension hstore add role app_owner; + +alter extension hstore add routine public.normalize(text); + +alter extension hstore add schema application; + +alter extension hstore add sequence public.account_ids; + +alter extension hstore add server remote_server; + +alter extension hstore add statistics public.account_stats; + +alter extension hstore add subscription account_subscription; + +alter extension hstore drop table public.old_accounts; + +alter extension hstore add tablespace fast_storage; + +alter extension hstore add text search configuration public.english_config; + +alter extension hstore add text search dictionary public.english_dict; + +alter extension hstore add text search parser public.english_parser; + +alter extension hstore add text search template public.english_template; + +alter extension hstore add transform for jsonb language plpgsql; + +alter extension hstore add type public.account_state; + +alter extension hstore add view public.active_accounts; + +alter /* extension */ extension /* name */ an_exceedingly_long_extension_name_for_application_features + /* action */ add + /* object kind */ operator + /* operator name */ application_schema.=== + /* open paren */( + /* left type */ application_schema.exceedingly_long_left_operand_type, + /* right type */ application_schema.exceedingly_long_right_operand_type + /* close paren */)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_foreign_data_wrapper.snap b/crates/squawk_fmt/tests/after/alter_foreign_data_wrapper.snap new file mode 100644 index 00000000..77a0d8fb --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_foreign_data_wrapper.snap @@ -0,0 +1,19 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_foreign_data_wrapper.sql +--- +alter /* foreign */ foreign /* data */ data /* wrapper */ wrapper /* name */ fdw + /* options */ options /* left */ ( + /* add */ add /* key */ host /* value */ 'localhost', + /* set */ set /* key */ port /* value */ '5432', + /* drop */ drop /* key */ obsolete /* right */ + ); + +alter foreign data wrapper very_long_foreign_data_wrapper_name + handler public.very_long_handler_function_name; + +alter foreign data wrapper fdw no validator; + +alter foreign data wrapper fdw rename /* to */ to /* name */ renamed_fdw; + +alter foreign data wrapper fdw owner /* to */ to /* role */ current_user; diff --git a/crates/squawk_fmt/tests/after/alter_foreign_table.snap b/crates/squawk_fmt/tests/after/alter_foreign_table.snap new file mode 100644 index 00000000..2e120694 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_foreign_table.snap @@ -0,0 +1,21 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_foreign_table.sql +--- +alter /* foreign */ foreign /* table */ table /* if */ if /* not */ exists /* table name */ public.very_long_foreign_table_name + add /* column */ column /* if */ if /* not */ not /* exists */ exists /* column name */ column_with_a_very_long_descriptive_name /* type */ varchar(255) + /* options */ options ( + /* key */ remote_name /* value */ 'remote_column' + ), + /* drop */ drop /* column */ column /* if */ if /* exists */ exists /* name */ obsolete_column /* behavior */ cascade; + +alter foreign table public.items + alter column quantity set default 100, + alter column description type text using description::text; + +alter foreign table public.items + add constraint positive_quantity check (quantity > 0), + rename column old_name to new_name; + +alter foreign table if exists public.items + owner to role_with_a_very_long_descriptive_name; diff --git a/crates/squawk_fmt/tests/after/alter_function.snap b/crates/squawk_fmt/tests/after/alter_function.snap new file mode 100644 index 00000000..f284e4fe --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_function.snap @@ -0,0 +1,31 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_function.sql +--- +alter /* function */ function /* signature */ public.calculate_a_very_long_and_descriptive_result( + /* type */ integer, + /* type */ text +) + /* action */ security /* definer */ definer + /* restrict */ restrict; + +alter function public.f( + integer +) + /* action */ set /* parameter */ work_mem /* to */ to /* value */ '64MB'; + +alter function public.f( + integer +) + depends /* on */ on /* extension */ extension /* name */ extension_name; + +alter function public.f( + integer +) + no /* depends */ depends /* on */ on /* extension */ extension /* name */ extension_name; + +alter function public.f(integer) rename /* to */ to /* name */ renamed_function; + +alter function public.f(integer) owner /* to */ to /* role */ current_role; + +alter function public.f(integer) set /* schema */ schema /* name */ archive; diff --git a/crates/squawk_fmt/tests/after/alter_group.snap b/crates/squawk_fmt/tests/after/alter_group.snap new file mode 100644 index 00000000..51f31731 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_group.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_group.sql +--- +alter /* group */ group /* group name */ administrators + /* action */ add /* user */ user + /* first */ alice, + /* second */ bob, + /* long */ user_with_an_exceptionally_long_name; + +alter group administrators + drop /* user */ user /* first */ alice, /* second */ bob; + +alter group administrators + rename /* to */ to /* name */ application_administrators_with_a_very_long_name; diff --git a/crates/squawk_fmt/tests/after/alter_index.snap b/crates/squawk_fmt/tests/after/alter_index.snap new file mode 100644 index 00000000..6731b1a8 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_index.snap @@ -0,0 +1,42 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_index.sql +--- +alter /* index */ index + /* all */ all /* in */ in /* tablespace */ tablespace /* old */ old_tablespace_with_a_very_long_descriptive_name + /* owned */ owned /* by */ by /* role one */ first_role_with_a_long_name, + /* role two */ second_role_with_a_long_name + /* set */ set /* tablespace */ tablespace /* new */ new_tablespace_with_a_very_long_descriptive_name + /* nowait */ nowait/* end */; + +alter index /* if */ if /* exists */ exists /* name */ public.index_with_a_very_long_descriptive_name + /* alter */ alter /* column */ column /* number */ 3 + /* set */ set /* statistics */ statistics /* value */ 1000; + +alter index idx attach /* partition */ partition /* child */ public.child_idx; + +alter index idx + /* depends */ depends /* on */ on /* extension */ extension /* name */ bloom; + +alter index idx + /* no */ no /* depends */ depends /* on */ on /* extension */ extension /* name */ bloom; + +alter index idx /* rename */ rename /* to */ to /* name */ renamed_idx; + +alter index idx + /* reset */ reset /* list */ ( + /* first */ fillfactor, + /* second */ deduplicate_items + ); + +alter index idx + /* set */ set /* list */ ( + /* key */ fillfactor /* equals */ = /* value */ 70, + deduplicate_items = off + ); + +alter index idx + alter column indexed_expression set (/* key */ n_distinct = /* value */ 100); + +alter index idx + /* set */ set /* tablespace */ tablespace /* name */ fast_storage; diff --git a/crates/squawk_fmt/tests/after/alter_language.snap b/crates/squawk_fmt/tests/after/alter_language.snap new file mode 100644 index 00000000..78aa440d --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_language.snap @@ -0,0 +1,13 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_language.sql +--- +alter /* procedural */ procedural /* language */ language /* name */ plpgsql + /* rename */ rename /* to */ to /* new name */ plpgsql_new_name_with_a_very_long_descriptive_suffix/* end */; + +alter /* language */ language /* name */ plpgsql + /* owner */ owner /* to */ to /* role */ language_owner_with_a_very_long_descriptive_name; + +alter language old_language rename to new_language; + +alter language plpgsql owner to current_user; diff --git a/crates/squawk_fmt/tests/after/alter_large_object.snap b/crates/squawk_fmt/tests/after/alter_large_object.snap new file mode 100644 index 00000000..278b98b6 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_large_object.snap @@ -0,0 +1,8 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_large_object.sql +--- +alter /* large */ large /* object */ object /* id */ 1234567890 + /* owner */ owner /* to */ to /* role */ large_object_owner_with_a_very_long_descriptive_name/* end */; + +alter large object 987654321 owner to current_role; diff --git a/crates/squawk_fmt/tests/after/alter_materialized_view.snap b/crates/squawk_fmt/tests/after/alter_materialized_view.snap new file mode 100644 index 00000000..41b870a9 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_materialized_view.snap @@ -0,0 +1,31 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_materialized_view.sql +--- +alter /* materialized */ materialized /* view */ view + /* all */ all /* in */ in /* tablespace */ tablespace /* old */ old_materialized_view_tablespace + /* owned */ owned /* by */ by /* role */ analytics_owner_with_a_very_long_name + /* set */ set /* tablespace */ tablespace /* new */ new_materialized_view_tablespace + /* nowait */ nowait/* end */; + +alter materialized view /* if */ if /* exists */ exists /* view */ reporting.materialized_view_with_a_very_long_descriptive_name + /* alter */ alter /* column */ column /* name */ measured_value + /* set */ set /* statistics */ statistics /* value */ 1000, + /* owner */ owner /* to */ to /* role */ analytics_owner_with_a_very_long_name; + +alter materialized view reporting.summary + /* rename */ rename /* column */ column /* old */ old_column /* to */ to /* new */ new_column; + +alter materialized view reporting.summary + /* rename */ rename /* to */ to /* view */ renamed_summary; + +alter materialized view reporting.summary + /* set */ set /* schema */ schema /* name */ archive; + +alter materialized view reporting.summary + /* depends */ depends /* on */ on /* extension */ extension /* name */ tablefunc; + +alter materialized view reporting.summary + /* no */ no /* depends */ depends /* on */ on /* extension */ extension /* name */ tablefunc; + +alter materialized view reporting.summary set (fillfactor = 80); diff --git a/crates/squawk_fmt/tests/after/alter_operator.snap b/crates/squawk_fmt/tests/after/alter_operator.snap new file mode 100644 index 00000000..e30990a8 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_operator.snap @@ -0,0 +1,20 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +assertion_line: 28 +input_file: crates/squawk_fmt/tests/before/alter_operator.sql +--- +alter /* operator */ operator /* signature */ public.+ /* left paren */( + /* left type */ integer /* comma */, + /* right type */ integer /* right paren */ +) + /* owner */ owner /* to */ to /* role */ application_owner/* end */; + +alter operator public.##(none, integer) set ( + restrict = schema_a.restrict_function_with_a_very_long_name, + "join" = schema_a.join_function_with_a_very_long_name, + hashes, + merges +); + +alter operator public.+(integer, integer) + /* set */ set /* schema */ schema /* name */ archive; diff --git a/crates/squawk_fmt/tests/after/alter_operator_class.snap b/crates/squawk_fmt/tests/after/alter_operator_class.snap new file mode 100644 index 00000000..68e6e79b --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_operator_class.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_operator_class.sql +--- +alter /* operator */ operator /* class */ class /* name */ public.integer_operations + /* using */ using /* method */ btree + /* rename */ rename /* to */ to /* new name */ integer_operations_for_archived_measurements/* end */; + +alter operator class public.integer_operations + using btree + owner to application_owner_with_a_very_long_descriptive_name; + +alter operator class public.integer_operations + using btree + /* set */ set /* schema */ schema /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/after/alter_operator_family.snap b/crates/squawk_fmt/tests/after/alter_operator_family.snap new file mode 100644 index 00000000..58825d6d --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_operator_family.snap @@ -0,0 +1,43 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_operator_family.sql +--- +alter /* operator */ operator /* family */ family /* name */ public.integer_family + /* using */ using /* method */ btree + /* add */ add + /* operator option */ operator /* strategy */ 1 /* op */ < /* left paren */( + /* lhs */ integer /* comma */, + /* rhs */ integer /* right paren */ + ) + /* for */ for /* order */ order /* by */ by /* family */ public.sort_family /* option comma */, + /* function option */ function /* support */ 1 /* argument types */ ( + integer, + integer + ) + /* function */ public.compare_integer_values( + integer, + integer + ) /* option comma */, + /* storage */ storage /* type */ bigint/* end */; + +alter operator family public.integer_family + using btree + /* drop */ drop + /* operator */ operator /* strategy */ 1 /* params */ ( + integer, + integer + ) /* comma */, + /* function */ function /* support */ 1 /* params */ ( + integer, + integer + ); + +alter operator family public.integer_family + using btree + rename to integer_family_for_archived_measurements; + +alter operator family public.integer_family + using btree + owner to application_owner; + +alter operator family public.integer_family using btree set schema archive; diff --git a/crates/squawk_fmt/tests/after/alter_policy.snap b/crates/squawk_fmt/tests/after/alter_policy.snap new file mode 100644 index 00000000..95c757b6 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_policy.snap @@ -0,0 +1,29 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_policy.sql +--- +alter /* policy */ policy /* name */ account_access_policy + /* on */ on /* table */ public.accounts + /* to */ to /* first role */ account_administrator, + /* second role */ account_auditor + /* using */ using /* left paren */ ( + /* expression */ owner_identifier = "current_user" /* right paren */ + ) + /* with */ with /* check */ check /* left paren */ ( + /* expression */ account_is_active + and account_identifier > 1000000 /* right paren */ + )/* end */; + +alter policy account_access_policy + on public.accounts + using ( + account_identifier + = a_very_long_session_account_identifier_function( + "current_user", + "current_role" + ) + ); + +alter policy account_access_policy + on public.accounts + /* rename */ rename /* to */ to /* new name */ archived_account_access_policy; diff --git a/crates/squawk_fmt/tests/after/alter_procedure.snap b/crates/squawk_fmt/tests/after/alter_procedure.snap new file mode 100644 index 00000000..6e6303de --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_procedure.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_procedure.sql +--- +alter /* procedure */ procedure /* signature */ public.recalculate_account_totals( + /* arg */ integer, + text +) + /* action */ rename /* to */ to /* target */ recalculate_totals/* end */; + +alter procedure process_customer_transactions_with_an_exceptionally_long_name( + integer, + text +) + security definer set work_mem = '256MB' reset all + restrict; + +alter procedure p no depends on extension /* extension */ auditing_tools; diff --git a/crates/squawk_fmt/tests/after/alter_property_graph.snap b/crates/squawk_fmt/tests/after/alter_property_graph.snap new file mode 100644 index 00000000..21ad21cd --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_property_graph.snap @@ -0,0 +1,35 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +assertion_line: 28 +input_file: crates/squawk_fmt/tests/before/alter_property_graph.sql +--- +alter /* property */ property /* graph */ graph /* if */ if /* exists */ exists /* name */ public.social_graph + /* action */ rename /* to */ to /* target */ archived_graph/* end */; + +alter property graph graph_with_an_exceptionally_long_descriptive_name + add vertex tables ( + customer_accounts key (customer_identifier) no properties, + merchant_accounts key (merchant_identifier) properties all columns + ); + +alter property graph social_graph + alter /* kind */ vertex /* table */ table /* element */ people alter /* label */ label /* name */ person add /* properties */ properties ( + full_name, + birth_date as date_of_birth + ); + +alter property graph social_graph + alter relationship table follows + alter label connection drop properties ( + created_at, + source_system + ); + +alter property graph social_graph + drop vertex tables ( + people, + organizations + ) cascade; + +alter property graph social_graph + owner /* to */ to /* role */ graph_administrator; diff --git a/crates/squawk_fmt/tests/after/alter_publication.snap b/crates/squawk_fmt/tests/after/alter_publication.snap index 37d33102..e2240a61 100644 --- a/crates/squawk_fmt/tests/after/alter_publication.snap +++ b/crates/squawk_fmt/tests/after/alter_publication.snap @@ -3,15 +3,15 @@ source: crates/squawk_fmt/tests/tests.rs input_file: crates/squawk_fmt/tests/before/alter_publication.sql --- alter publication selected_tables -add table public.new_accounts, tables in schema archive; + add table public.new_accounts, tables in schema archive; alter publication selected_tables drop table public.old_accounts; alter publication selected_tables -set table public.accounts (id) where (id > 200); + set table public.accounts (id) where (id > 200); alter publication everything -set all tables, all sequences except (table private.tokens, audit.logs); + set all tables, all sequences except (table private.tokens, audit.logs); alter publication everything set (publish = 'insert, update'); @@ -20,6 +20,6 @@ alter publication everything owner to replication_admin; alter publication everything rename to all_changes; alter /* after alter */ publication /* before name */ commented_pub -set - /* before object */ table /* before table name */ public.commented, - /* after comma */ current_schema/* before semicolon */; + set + /* before object */ table /* before table name */ public.commented, + /* after comma */ current_schema/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_role.snap b/crates/squawk_fmt/tests/after/alter_role.snap new file mode 100644 index 00000000..59259079 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_role.snap @@ -0,0 +1,22 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_role.sql +--- +alter /* role */ role /* name */ reporting_user + /* in */ in /* database */ database /* db */ analytics + /* action */ set /* parameter */ work_mem /* assignment */ to /* value */ '128MB'/* end */; + +alter role exceptionally_long_application_service_account_name + with superuser + createdb + createrole + login + replication + bypassrls + connection limit 100 + encrypted password 'a-long-secret-value' + valid until '2042-02-22'; + +alter role current_user rename /* to */ to /* target */ renamed_user; + +alter role all reset /* all */ all; diff --git a/crates/squawk_fmt/tests/after/alter_routine.snap b/crates/squawk_fmt/tests/after/alter_routine.snap new file mode 100644 index 00000000..d2ccf2d3 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_routine.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_routine.sql +--- +alter /* routine */ routine /* signature */ public.calculate_customer_balance( + /* arg */ integer +) + /* action */ owner /* to */ to /* owner */ current_user/* end */; + +alter routine calculate_customer_balance_for_all_historical_transactions( + integer, + text +) + immutable parallel safe cost 1000 rows 10000 + restrict; + +alter routine r + /* no */ no depends /* on */ on extension /* name */ helper_extension; diff --git a/crates/squawk_fmt/tests/after/alter_rule.snap b/crates/squawk_fmt/tests/after/alter_rule.snap new file mode 100644 index 00000000..b271a55e --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_rule.snap @@ -0,0 +1,13 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_rule.sql +--- +alter rule old_rule on public.orders rename to current_orders; + +alter rule rule_with_a_very_long_descriptive_name + on reporting.relation_with_a_very_long_descriptive_name + rename to renamed_rule_with_a_very_long_descriptive_name; + +alter /* before rule */ rule /* before old name */ commented_rule + /* before on */ on /* before relation */ public.commented_orders + /* before rename */ rename /* before to */ to /* before new name */ renamed_commented_rule/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_schema.snap b/crates/squawk_fmt/tests/after/alter_schema.snap new file mode 100644 index 00000000..aff6829c --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_schema.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_schema.sql +--- +alter schema accounting rename to finance; + +alter schema reporting owner to reporting_admin; + +alter schema schema_with_a_very_long_descriptive_name + rename to schema_with_an_even_longer_descriptive_replacement_name; + +alter /* before schema */ schema /* before name */ commented_schema + /* before rename */ rename /* before to */ to /* before new name */ renamed_commented_schema/* before semicolon */; + +alter schema another_commented_schema + /* before owner */ owner /* before to */ to /* before role */ current_user; diff --git a/crates/squawk_fmt/tests/after/alter_sequence.snap b/crates/squawk_fmt/tests/after/alter_sequence.snap new file mode 100644 index 00000000..0909bf0e --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_sequence.snap @@ -0,0 +1,45 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_sequence.sql +--- +alter sequence public.invoice_number + increment by 10 + minvalue 1 + maxvalue 999999999 + start with 100 + cache 50 + cycle + owned by public.invoices.invoice_number; + +alter sequence if exists public.invoice_number + as bigint + no minvalue + no maxvalue + restart with 500 + no cycle + owned by none; + +alter sequence public.invoice_number set logged; + +alter sequence public.invoice_number set unlogged; + +alter sequence public.invoice_number owner to sequence_admin; + +alter sequence public.invoice_number rename to archived_invoice_number; + +alter sequence public.invoice_number set schema archive; + +alter /* before sequence */ sequence /* before if */ if /* before exists */ exists /* before name */ public.commented_sequence + /* before increment */ increment /* before by */ by /* before value */ 100 + /* before minvalue */ minvalue /* before minimum */ 1 + /* before maxvalue */ maxvalue /* before maximum */ 999999 + /* before start */ start /* before with */ with /* before start value */ 10 + /* before cache */ cache /* before cache value */ 20 + /* before cycle */ no /* before cycle keyword */ cycle + /* before owned */ owned /* before owned by */ by /* before owner target */ public.orders.sequence_id/* before semicolon */; + +alter sequence public.other_sequence + /* before set */ set /* before logged */ logged; + +alter sequence public.third_sequence + /* before owner */ owner /* before to */ to /* before role */ current_role; diff --git a/crates/squawk_fmt/tests/after/alter_server.snap b/crates/squawk_fmt/tests/after/alter_server.snap new file mode 100644 index 00000000..ee5fc450 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_server.snap @@ -0,0 +1,34 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_server.sql +--- +alter server analytics + version 'postgresql-17' + options ( + add host 'analytics.internal.example', + set port '5432', + drop obsolete_setting + ); + +alter server analytics owner to foreign_data_admin; + +alter server analytics rename to analytics_primary; + +alter server foreign_server_with_a_very_long_descriptive_name + version 'postgresql-17-with-a-very-long-version-description' + options ( + add host 'database-with-a-long-name.internal.example', + set application_name 'squawk formatter integration test', + drop obsolete_connection_setting + ); + +alter /* before server */ server /* before name */ commented_server + /* before version */ version /* before version value */ '17.0' + /* before options */ options /* before left paren */ ( + /* before add */ add /* before option name */ host /* before option value */ 'db.internal', + /* after comma */ set /* before set name */ port /* before set value */ '5432', + /* second comma */ drop /* before drop name */ old_port /* before right paren */ + )/* before semicolon */; + +alter server another_commented_server + /* before owner */ owner /* before to */ to /* before role */ session_user; diff --git a/crates/squawk_fmt/tests/after/alter_statistics.snap b/crates/squawk_fmt/tests/after/alter_statistics.snap new file mode 100644 index 00000000..bbbd9d6c --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_statistics.snap @@ -0,0 +1,22 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_statistics.sql +--- +alter statistics reporting.user_activity_statistics + owner to analytics_administrator; + +alter statistics reporting.user_activity_statistics + rename to user_activity_statistics_for_archived_reporting; + +alter statistics reporting.user_activity_statistics + set schema reporting_archive; + +alter statistics reporting.user_activity_statistics set statistics 1000; + +alter statistics reporting.user_activity_statistics set statistics default; + +alter /* statistics */ statistics /* name */ reporting.commented_statistics + /* action */ rename /* to */ to /* new name */ reporting.renamed_commented_statistics/* semicolon */; + +alter statistics reporting.statistics_with_a_very_long_and_descriptive_name + set statistics /* expression */ 10000; diff --git a/crates/squawk_fmt/tests/after/alter_subscription.snap b/crates/squawk_fmt/tests/after/alter_subscription.snap index a73efe5d..56918655 100644 --- a/crates/squawk_fmt/tests/after/alter_subscription.snap +++ b/crates/squawk_fmt/tests/after/alter_subscription.snap @@ -7,15 +7,17 @@ alter subscription local_sub connection 'host=otherhost dbname=publisher'; alter subscription local_sub server publisher_server; alter subscription local_sub -set (slot_name = new_slot, synchronous_commit = local); + set (slot_name = new_slot, synchronous_commit = local); alter subscription local_sub -add publication another_publication, third_publication with (copy_data = false); + add publication another_publication, + third_publication + with (copy_data = false); alter subscription local_sub set publication all_changes with (refresh = true); alter subscription local_sub -drop publication selected_tables with (refresh = false); + drop publication selected_tables with (refresh = false); alter subscription local_sub refresh publication with (copy_data = true); @@ -30,6 +32,6 @@ alter subscription local_sub owner to replication_admin; alter subscription local_sub rename to renamed_sub; alter /* after alter */ subscription /* after subscription */ renamed_sub -add /* after add */ publication /* before publication name */ commented_pub, - /* after publication comma */ selected_tables -with /* before params */ (copy_data = true)/* before semicolon */; + add /* after add */ publication /* before publication name */ commented_pub, + /* after publication comma */ selected_tables + with /* before params */ (copy_data = true)/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_system.snap b/crates/squawk_fmt/tests/after/alter_system.snap new file mode 100644 index 00000000..e6d4a125 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_system.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_system.sql +--- +alter system set work_mem = '64MB'; + +alter system + set archive_command to 'cp %p /very/long/archive/destination/with/a/descriptive/directory/%f'; + +alter system + set shared_preload_libraries = 'pg_stat_statements', + /* second value */ 'auto_explain'; + +alter system set effective_cache_size from current; + +alter system reset work_mem; + +alter system reset all; + +alter /* system */ system + /* action */ set /* parameter */ autovacuum_vacuum_cost_limit /* equals */ = /* value */ 4000/* semicolon */; + +alter /* system */ system + /* reset */ reset /* parameter */ autovacuum_analyze_scale_factor/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_table.snap b/crates/squawk_fmt/tests/after/alter_table.snap new file mode 100644 index 00000000..ee35eb99 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_table.snap @@ -0,0 +1,52 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +assertion_line: 28 +input_file: crates/squawk_fmt/tests/before/alter_table.sql +--- +alter table customers + add column email_address varchar(320), + alter column customer_description + set default 'No description has been provided for this customer', + drop column if exists obsolete_customer_code cascade; + +alter table if exists only (reporting.customer_activity) + rename column old_activity_description to new_activity_description; + +alter table reporting.customer_activity + add constraint positive_activity_count check (activity_count > 0), + alter constraint positive_activity_count deferrable initially deferred; + +alter table reporting.customer_activity + attach partition reporting.customer_activity_2025 + for values from ('2025-01-01') to ('2026-01-01'); + +alter table reporting.customer_activity + detach partition reporting.customer_activity_2024 concurrently; + +alter table reporting.customer_activity + enable row level security, + force row level security, + disable trigger all; + +alter table reporting.customer_activity + set (fillfactor = 80), + reset (fillfactor), + set tablespace archive_tablespace; + +alter table all in tablespace old_reporting_tablespace + owned by analytics_owner, + reporting_owner + set tablespace new_reporting_tablespace + nowait; + +alter /* table */ table /* if */ if /* exists */ exists /* only */ only /* left paren */(/* relation */ reporting.commented_customer_activity /* right paren */) + /* action */ alter /* column */ column /* column name */ activity_description + /* set */ set /* default */ default + /* expression */ 'A very long default activity description used to verify formatter wrapping behavior', + /* comma */ rename /* column */ column /* old name */ old_activity_code /* to */ to /* new name */ current_activity_code/* semicolon */; + +alter /* table */ table /* all */ all /* in */ in /* tablespace keyword */ tablespace /* old tablespace */ old_tablespace + /* owned */ owned /* by */ by /* first role */ role_one, + /* second role */ role_two + /* set */ set /* tablespace */ tablespace /* new tablespace */ new_tablespace + /* nowait */ nowait/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_tablespace.snap b/crates/squawk_fmt/tests/after/alter_tablespace.snap new file mode 100644 index 00000000..fd004c64 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_tablespace.snap @@ -0,0 +1,36 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_tablespace.sql +--- +alter tablespace application_data owner to storage_administrator; + +alter tablespace application_data + rename to application_data_for_archived_customer_records; + +alter tablespace application_data + set ( + seq_page_cost = 1.0, + random_page_cost = 1.1, + effective_io_concurrency = 200 + ); + +alter tablespace application_data + reset (seq_page_cost, random_page_cost, effective_io_concurrency); + +alter /* tablespace */ tablespace /* name */ commented_application_data + /* action */ owner /* to */ to /* owner */ storage_administrator_with_a_very_long_name/* semicolon */; + +alter /* tablespace */ tablespace /* name */ commented_options + /* action */ set /* left paren */ ( + /* option */ seq_page_cost /* equals */ = /* value */ 1.0, + /* second option */ random_page_cost /* second equals */ = /* second value */ 1.1 /* right paren */ + )/* semicolon */; + +alter /* tablespace */ tablespace /* name */ commented_reset + /* action */ reset /* left paren */ ( + /* option */ seq_page_cost, + /* second option */ random_page_cost /* right paren */ + )/* semicolon */; + +alter /* tablespace */ tablespace /* old name */ commented_old_name + /* rename */ rename /* to */ to /* new name */ commented_new_name/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/alter_text_search_configuration.snap b/crates/squawk_fmt/tests/after/alter_text_search_configuration.snap new file mode 100644 index 00000000..ebff0c68 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_text_search_configuration.snap @@ -0,0 +1,49 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_text_search_configuration.sql +--- +alter /* text */ text /* search */ search /* configuration */ configuration /* name */ public.english + /* add */ add /* mapping */ mapping /* for */ for + /* kind one */ asciiword /* kind comma */, + /* kind two */ asciihword + /* with */ with + /* dictionary one */ public.simple /* dictionary comma */, + /* dictionary two */ public.english_stem/* end */; + +alter text search configuration public.english + alter /* mapping */ mapping + /* for */ for + /* kind */ word + /* with */ with + /* dictionary */ public.simple; + +alter text search configuration public.english + alter mapping + for + asciiword, + word + replace /* old dictionary */ public.old_dictionary + /* with */ with + /* new dictionary */ archive.new_dictionary; + +alter text search configuration public.english + alter mapping + /* replace */ replace /* old */ public.old_dictionary + /* with */ with + /* new */ public.new_dictionary; + +alter text search configuration public.english + drop /* mapping */ mapping + /* if */ if /* exists */ exists + /* for */ for + /* kind */ url /* comma */, + /* second kind */ host; + +alter text search configuration public.english + owner /* to */ to /* role */ application_owner; + +alter text search configuration public.english + set /* schema */ schema /* schema name */ archive; + +alter text search configuration public.english + rename /* to */ to /* new name */ english_configuration_for_archived_documents_and_historical_search_results; diff --git a/crates/squawk_fmt/tests/after/alter_text_search_dictionary.snap b/crates/squawk_fmt/tests/after/alter_text_search_dictionary.snap new file mode 100644 index 00000000..3162eff8 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_text_search_dictionary.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +assertion_line: 28 +input_file: crates/squawk_fmt/tests/before/alter_text_search_dictionary.sql +--- +alter /* text */ text /* search */ search /* dictionary */ dictionary /* name */ public.english /* left paren */ ( + /* first option */ stopwords /* equals */ = /* first value */ english /* comma */, + /* second option */ language /* second equals */ = /* second value */ 'english' /* right paren */ +)/* end */; + +alter text search dictionary public.english ( + stopwords = english, + accept = false, + long_dictionary_configuration_option_name = 'a very long dictionary configuration value for wrapping' +); + +alter text search dictionary public.english + owner /* to */ to /* owner */ application_owner; + +alter text search dictionary public.english + set /* schema */ schema /* schema */ archive; + +alter text search dictionary public.english + rename /* to */ to /* name */ english_dictionary_for_archived_documents_and_historical_search_results; diff --git a/crates/squawk_fmt/tests/after/alter_text_search_parser.snap b/crates/squawk_fmt/tests/after/alter_text_search_parser.snap new file mode 100644 index 00000000..4a68fa7d --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_text_search_parser.snap @@ -0,0 +1,9 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_text_search_parser.sql +--- +alter /* text */ text /* search */ search /* parser */ parser /* name */ public.default_parser + /* rename */ rename /* to */ to /* new name */ parser_for_archived_documents_and_historical_search_results/* end */; + +alter text search parser public.default_parser + set /* schema */ schema /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/after/alter_text_search_template.snap b/crates/squawk_fmt/tests/after/alter_text_search_template.snap new file mode 100644 index 00000000..5e0b09d6 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_text_search_template.snap @@ -0,0 +1,9 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_text_search_template.sql +--- +alter /* text */ text /* search */ search /* template */ template /* name */ public.default_template + /* rename */ rename /* to */ to /* new name */ template_for_archived_documents_and_historical_search_results/* end */; + +alter text search template public.default_template + set /* schema */ schema /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/after/alter_trigger.snap b/crates/squawk_fmt/tests/after/alter_trigger.snap new file mode 100644 index 00000000..def5a1ce --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_trigger.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_trigger.sql +--- +alter /* trigger */ trigger /* name */ audit_trigger + /* on */ on /* relation */ public.accounts + /* action */ no /* depends */ depends /* on extension */ on /* extension */ extension /* extension name */ audit_support/* end */; + +alter trigger audit_trigger + on public.accounts + depends on extension exceptionally_long_auditing_extension_name_for_regulated_customer_accounts; + +alter trigger audit_trigger + on public.accounts + rename /* to */ to /* target */ renamed_audit_trigger; diff --git a/crates/squawk_fmt/tests/after/alter_type.snap b/crates/squawk_fmt/tests/after/alter_type.snap new file mode 100644 index 00000000..22054348 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_type.snap @@ -0,0 +1,38 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_type.sql +--- +alter /* type */ type /* name */ public.delivery_status + /* action */ add /* value */ value /* if */ if /* not */ not /* exists */ exists /* new value */ 'an exceptionally long delivery status awaiting additional customer confirmation' + /* before */ before /* existing value */ 'delivered'/* end */; + +alter type public.delivery_status add value 'returned' after 'delivered'; + +alter type public.delivery_status + rename /* value */ value /* old */ 'awaiting' /* to */ to /* new */ 'pending'; + +alter type public.customer_address + add /* attribute */ attribute /* name */ street_name /* type */ character varying(255) + /* collate */ collate /* collation */ "C" /* behavior */ cascade, + /* next */ alter /* attribute */ attribute /* field */ postal_code /* set */ set /* data */ data /* type */ type /* new type */ character varying(32) /* behavior */ restrict, + /* next */ drop /* attribute */ attribute /* if */ if /* exists */ exists /* field */ obsolete_field /* behavior */ cascade; + +alter type public.customer_address + alter attribute country_code type text collate "C"; + +alter type public.customer_address + rename /* attribute */ attribute /* old */ street_name /* to */ to /* new */ address_line /* behavior */ cascade; + +alter type public.customer_address owner /* to */ to /* owner */ current_user; + +alter type public.customer_address + set /* options */ ( + /* receive */ receive = /* function */ public.receive_customer_address, + /* send */ send = public.send_customer_address + ); + +alter type public.customer_address + set /* schema */ schema /* target */ archived_customer_data; + +alter type public.customer_address + rename /* to */ to /* target */ archived_customer_address; diff --git a/crates/squawk_fmt/tests/after/alter_user.snap b/crates/squawk_fmt/tests/after/alter_user.snap new file mode 100644 index 00000000..4cd7f5cf --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_user.snap @@ -0,0 +1,23 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_user.sql +--- +alter /* user */ user /* role */ reporting_application_user + /* in */ in /* database */ database /* name */ exceptionally_long_analytics_database_name + /* action */ set /* parameter */ work_mem /* assignment */ to /* value */ '128MB'/* end */; + +alter user reporting_application_user + with superuser + createdb + createrole + login + replication + bypassrls + connection limit 100 + encrypted password 'an-exceptionally-long-secret-value' + valid until '2042-02-22'; + +alter user reporting_application_user + rename /* to */ to /* target */ renamed_reporting_application_user; + +alter user all in database analytics reset /* all */ all; diff --git a/crates/squawk_fmt/tests/after/alter_user_mapping.snap b/crates/squawk_fmt/tests/after/alter_user_mapping.snap new file mode 100644 index 00000000..68a4a940 --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_user_mapping.snap @@ -0,0 +1,17 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_user_mapping.sql +--- +alter /* user */ user /* mapping */ mapping /* for */ for /* role */ current_user + /* server */ server /* server name */ exceptionally_long_foreign_data_server_for_customer_analytics + /* options */ options /* left */ ( + /* add */ add /* option */ authentication_token /* value */ 'an-exceptionally-long-authentication-token-value', + /* set */ set /* option */ endpoint /* value */ 'https://analytics.example.test/foreign-data', + /* drop */ drop /* option */ obsolete_setting /* right */ + )/* end */; + +alter user mapping for public + server short_server + options ( + set user_name 'reporter' + ); diff --git a/crates/squawk_fmt/tests/after/alter_view.snap b/crates/squawk_fmt/tests/after/alter_view.snap new file mode 100644 index 00000000..e55c943f --- /dev/null +++ b/crates/squawk_fmt/tests/after/alter_view.snap @@ -0,0 +1,29 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/alter_view.sql +--- +alter view public.orders alter column status set default 'pending'; + +alter view if exists public.orders alter column status drop default; + +alter view public.orders + rename column customer_identifier to account_identifier; + +alter view public.orders owner to reporting_role; + +alter view public.orders set schema reporting; + +alter view public.orders rename to archived_orders; + +alter view public.orders + set (security_barrier = true, security_invoker = false); + +alter view public.orders reset (security_barrier, security_invoker); + +alter /* view keyword */ view /* if exists */ if /* exists keyword */ exists /* view name */ extraordinarily_long_schema_name.extraordinarily_long_view_name + /* action */ alter /* column keyword */ column /* column name */ extraordinarily_long_column_name + /* set keyword */ set /* default keyword */ default + /* expression */ coalesce( + current_setting('app.default_status'), + 'pending' + )/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/comment_on.snap b/crates/squawk_fmt/tests/after/comment_on.snap new file mode 100644 index 00000000..758c3271 --- /dev/null +++ b/crates/squawk_fmt/tests/after/comment_on.snap @@ -0,0 +1,33 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/comment_on.sql +--- +comment on table public.orders is 'customer orders'; + +comment on column public.orders.customer_identifier + is 'external customer identifier'; + +comment on constraint positive_amount on domain public.positive_money + is 'amount must be positive'; + +comment on policy tenant_isolation + on extraordinarily_long_schema_name.extraordinarily_long_table_name + is 'restrict rows to the current tenant'; + +comment on rule refresh_summary on public.orders is null; + +comment on trigger update_timestamp on public.orders is 'maintains updated_at'; + +comment on large object 123456789 is 'archived document'; + +comment on operator public.+ (integer, integer) is 'integer addition'; + +comment on operator class public.text_ops using btree is 'text operator class'; + +comment on transform for jsonb language plpgsql is 'json transform'; + +comment on property graph public.sales_graph is 'sales relationships'; + +comment /* on keyword */ on /* object kind */ materialized /* view keyword */ view + /* object name */ extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name + /* is keyword */ is /* comment value */ 'A deliberately long description of the materialized view used to verify wrapping behavior.'/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_access_method.snap b/crates/squawk_fmt/tests/after/create_access_method.snap new file mode 100644 index 00000000..286eff2a --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_access_method.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_access_method.sql +--- +create access method heap2 + type table + handler public.heap2_handler; + +create access method extraordinarily_long_access_method_name + type index + handler extraordinarily_long_schema_name.extraordinarily_long_handler_function_name; + +create /* access keyword */ access /* method keyword */ method /* name */ commented_method + /* type keyword */ type /* method type */ index + /* handler keyword */ handler /* handler name */ public.commented_handler/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_aggregate.snap b/crates/squawk_fmt/tests/after/create_aggregate.snap new file mode 100644 index 00000000..7e7797ad --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_aggregate.snap @@ -0,0 +1,55 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_aggregate.sql +--- +create aggregate public.sum_of_squares(integer) ( + sfunc = public.sum_squares_state, + stype = bigint, + initcond = '0' +); + +create or replace aggregate public.count_any(*) ( + sfunc = public.count_state, + stype = bigint +); + +create aggregate public.old_style_sum ( + sfunc = public.sum_state, + basetype = integer, + stype = bigint +); + +create or replace aggregate extraordinarily_long_schema_name.extraordinarily_long_aggregate_name( + double precision, + numeric, + bigint +) ( + sfunc = extraordinarily_long_schema_name.extraordinarily_long_transition_function_name, + stype = extraordinarily_long_schema_name.extraordinarily_long_state_type_name, + parallel = safe +); + +create /* or replace */ or /* replace keyword */ replace /* aggregate keyword */ aggregate /* name */ public.commented_aggregate( + /* parameter */ integer +) /* attributes */ ( + /* first attribute */ sfunc /* equals */ = /* value */ public.commented_state, + /* second attribute */ stype = /* type value */ bigint /* closing parenthesis */ +)/* semicolon */; + +create aggregate public.ordered_set_aggregate( + /* direct parameter */ double precision + /* order keyword */ order /* by keyword */ by /* first ordered parameter */ anyelement, + /* second ordered parameter */ text /* closing parenthesis */ +) ( + sfunc = public.ordered_set_state, + stype = internal +); + +create aggregate extraordinarily_long_schema_name.extraordinarily_long_ordered_set_aggregate_name( + extraordinarily_long_schema_name.extraordinarily_long_direct_argument_type + order by extraordinarily_long_schema_name.extraordinarily_long_first_ordered_argument_type, + extraordinarily_long_schema_name.extraordinarily_long_second_ordered_argument_type +) ( + sfunc = extraordinarily_long_schema_name.extraordinarily_long_transition_function_name, + stype = internal +); diff --git a/crates/squawk_fmt/tests/after/create_cast.snap b/crates/squawk_fmt/tests/after/create_cast.snap new file mode 100644 index 00000000..31a64917 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_cast.snap @@ -0,0 +1,39 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_cast.sql +--- +create cast (integer as bigint) + with function public.integer_to_bigint(integer) + as assignment; + +create cast (text as varchar) without function as implicit; + +create cast (json as jsonb) with inout; + +create cast ( + extraordinarily_long_schema_name.extraordinarily_long_source_type_name + as + extraordinarily_long_schema_name.extraordinarily_long_target_type_name +) + with function + extraordinarily_long_schema_name.extraordinarily_long_conversion_function_name( + extraordinarily_long_schema_name.extraordinarily_long_source_type_name + ) + as implicit; + +create /* cast keyword */ cast /* left parenthesis */ ( + /* source type */ text + /* as keyword */ as + /* target type */ varchar + /* right parenthesis */) + /* method */ with /* function keyword */ function + /* function name */ public.convert_text( + /* argument type */ text /* argument closing parenthesis */ + ) + /* context */ as /* assignment keyword */ assignment/* semicolon */; + +create cast (integer as text) + /* method */ without /* function keyword */ function + /* context */ as /* implicit keyword */ implicit; + +create cast (xml as text) /* method */ with /* inout keyword */ inout; diff --git a/crates/squawk_fmt/tests/after/create_collation.snap b/crates/squawk_fmt/tests/after/create_collation.snap new file mode 100644 index 00000000..f827ed99 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_collation.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_collation.sql +--- +create collation public.english ( + locale = 'en_US.UTF-8', + provider = icu, + deterministic = false +); + +create collation if not exists public.english_copy from public.english; + +create collation extraordinarily_long_schema_name.extraordinarily_long_collation_name ( + locale = 'en-US-u-va-posix', + provider = icu, + rules = '&V << w <<< W', + version = '153.120' +); + +create /* collation keyword */ collation /* if keyword */ if /* not keyword */ not /* exists keyword */ exists /* name */ public.commented /* left parenthesis */ ( + /* first option */ locale /* equals */ = /* value */ 'en_US.UTF-8' /* comma */, + /* second option */ provider = /* provider value */ icu /* closing parenthesis */ +)/* semicolon */; + +create collation /* name */ public.commented_copy + /* from clause */ from /* source collation */ public.original/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_conversion.snap b/crates/squawk_fmt/tests/after/create_conversion.snap new file mode 100644 index 00000000..e6623581 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_conversion.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_conversion.sql +--- +create conversion public.latin_to_utf8 + for 'LATIN1' + to 'UTF8' + from public.latin1_to_utf8; + +create default conversion public.default_latin_to_utf8 + for 'LATIN1' + to 'UTF8' + from public.latin1_to_utf8; + +create conversion extraordinarily_long_schema_name.extraordinarily_long_conversion_name + for 'EXTRAORDINARILY_LONG_SOURCE_ENCODING' + to 'EXTRAORDINARILY_LONG_DESTINATION_ENCODING' + from + extraordinarily_long_schema_name.extraordinarily_long_conversion_function_name; + +create /* default keyword */ default /* conversion keyword */ conversion /* name */ public.commented_conversion + /* for keyword */ for /* source encoding */ 'LATIN1' + /* to keyword */ to /* destination encoding */ 'UTF8' + /* from keyword */ from /* function */ public.convert_encoding/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_database.snap b/crates/squawk_fmt/tests/after/create_database.snap new file mode 100644 index 00000000..3357b0c3 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_database.snap @@ -0,0 +1,38 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_database.sql +--- +create database app; + +create database app_copy + with + owner = app_owner + template = template0 + encoding = 'UTF8' + locale = 'en_US.UTF-8' + tablespace = app_space + connection limit = 100; + +create database extraordinarily_long_database_name + with + owner = extraordinarily_long_database_owner + template = extraordinarily_long_template_database + encoding = 'UTF8' + strategy = wal_log + locale_provider = icu + icu_locale = 'en-US-u-va-posix' + collation_version = '153.120' + tablespace = extraordinarily_long_tablespace_name + allow_connections = true + connection limit = 250 + is_template = false + oid = 16384; + +create /* database keyword */ database /* database name */ commented_database + /* with keyword */ with + /* owner option */ owner /* equals */ = /* owner value */ commented_owner + /* template option */ template = /* template value */ template0 + /* encoding option */ encoding = /* encoding value */ 'UTF8' + /* generic option */ locale /* generic equals */ = /* generic value */ 'en_US.UTF-8' + /* connection option */ connection /* limit keyword */ limit = /* limit value */ 42 + /* tablespace option */ tablespace = /* tablespace value */ commented_space/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_domain.snap b/crates/squawk_fmt/tests/after/create_domain.snap new file mode 100644 index 00000000..6db98411 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_domain.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_domain.sql +--- +create domain public.email_address + as varchar(255) + collate "C" + default 'unknown@example.com' + constraint email_address_valid check (value like '%@%') + not null; + +create domain extraordinarily_long_schema_name.extraordinarily_long_domain_name + as character varying(1024) + collate extraordinarily_long_schema_name.extraordinarily_long_collation_name + default 'an extraordinarily long default value that forces wrapping' + check (length(value) > 3) + not null; + +/* before create */ +create /* before domain */ domain /* before name */ public.commented_domain + /* before as */ as /* before type */ text + /* before collate */ collate /* before collation */ "C" + /* before constraint */ constraint /* before constraint name */ valid_value /* before check */ check /* before left paren */ ( + /* before expression */ value <> '' /* before right paren */ + ) + /* before not */ not /* before null */ null/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_event_trigger.snap b/crates/squawk_fmt/tests/after/create_event_trigger.snap new file mode 100644 index 00000000..e146ca83 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_event_trigger.snap @@ -0,0 +1,29 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_event_trigger.sql +--- +create event trigger ddl_watch + on ddl_command_start + execute function audit.log_ddl(); + +create event trigger extraordinarily_long_event_trigger_name + on ddl_command_end + when tag in ('CREATE TABLE', 'ALTER TABLE', 'CREATE MATERIALIZED VIEW') + and extension in ('extraordinarily_long_extension_name') + execute procedure + extraordinarily_long_schema_name.extraordinarily_long_event_trigger_procedure(); + +/* before create */ +create /* before event */ event /* before trigger */ trigger /* before name */ commented_trigger + /* before on */ on /* before event name */ ddl_command_end + /* before when */ when /* before variable */ tag /* before in */ in /* before left paren */ ( + /* before first literal */ 'CREATE TABLE' /* before comma */, + /* after comma */ 'ALTER TABLE' /* before right paren */ + ) + /* before and */ and /* before second variable */ extension /* before second in */ in /* before second left paren */ ( + /* before second literal */ 'postgis' /* before second right paren */ + ) + /* before execute */ execute /* before function */ function + /* before call */ audit.log_ddl /* before call left paren */( + /* before call right paren */ + )/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_extension.snap b/crates/squawk_fmt/tests/after/create_extension.snap new file mode 100644 index 00000000..726e82ab --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_extension.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_extension.sql +--- +create extension postgis; + +create extension if not exists postgis + with + schema extensions + version '3.4.2' + cascade; + +create extension if not exists extraordinarily_long_extension_name + with + schema extraordinarily_long_schema_name + version 'extraordinarily-long-extension-version-number' + cascade; + +/* before create */ +create /* before extension */ extension /* before if */ if /* before not */ not /* before exists */ exists /* before name */ commented_extension + /* before with */ with + /* before schema */ schema /* before schema name */ extensions + /* before version */ version /* before version value */ '1.2.3' + /* before cascade */ cascade/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_foreign_data_wrapper.snap b/crates/squawk_fmt/tests/after/create_foreign_data_wrapper.snap new file mode 100644 index 00000000..9fc2376a --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_foreign_data_wrapper.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_foreign_data_wrapper.sql +--- +create foreign data wrapper local_fdw no handler no validator; + +create foreign data wrapper postgres_fdw + handler public.postgres_fdw_handler + validator public.postgres_fdw_validator + options (host 'localhost', port '5432'); + +create foreign data wrapper extraordinarily_long_foreign_data_wrapper_name + handler extraordinarily_long_schema_name.extraordinarily_long_handler_function + validator extraordinarily_long_schema_name.extraordinarily_long_validator_function + options ( + extraordinarily_long_option_name 'extraordinarily long foreign data wrapper option value' + ); + +/* before create */ +create /* before foreign */ foreign /* before data */ data /* before wrapper */ wrapper /* before name */ commented_fdw + /* before handler */ handler /* before handler name */ public.handler_fn + /* before validator */ validator /* before validator name */ public.validator_fn + /* before options */ options /* before left paren */ ( + /* before option name */ host /* before option value */ 'localhost' /* before comma */, + /* after comma */ port /* before second value */ '5432' /* before right paren */ + )/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_foreign_table.snap b/crates/squawk_fmt/tests/after/create_foreign_table.snap index 1511c878..bef12842 100644 --- a/crates/squawk_fmt/tests/after/create_foreign_table.snap +++ b/crates/squawk_fmt/tests/after/create_foreign_table.snap @@ -42,12 +42,12 @@ create foreign table an_intentionally_long_schema_name.an_intentionally_long_for ); /* before create */ -create /* before foreign */ foreign /* before table */ table /* before if */ if /* before not */ not /* before exists */ exists /* before table name */ public /* before dot */./* after dot */ remote_records/* before left paren */ ( +create /* before foreign */ foreign /* before table */ table /* before if */ if /* before not */ not /* before exists */ exists /* before table name */ public /* before dot */./* after dot */ remote_records /* before left paren */ ( /* after left paren */ id /* before type */ bigint /* before comma */, /* after comma */ name /* before second type */ text /* before right paren */ ) /* before server */ server /* before server name */ foreign_server - /* before options */ options /* before options left paren */( + /* before options */ options /* before options left paren */ ( /* after options left paren */ schema_name /* before option value */ 'public' /* before option comma */, /* after option comma */ table_name /* before second option value */ 'records' /* before options right paren */ )/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_group.snap b/crates/squawk_fmt/tests/after/create_group.snap new file mode 100644 index 00000000..ef7ad4a2 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_group.snap @@ -0,0 +1,32 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_group.sql +--- +create group analysts; + +create group + extraordinarily_long_group_name_for_business_intelligence_and_reporting + with superuser + createdb + createrole + inherit + login + replication + bypassrls + connection limit 250 + encrypted password 'an extraordinarily long password value used to exercise formatter wrapping' + valid until '2042-02-22' + in role analysts, + developers, + administrators + admin current_user, + session_user; + +-- comments in every position +create /* group */ group + /* name */ commented_group + /* options */ with /* generic option */ superuser + /* connection */ connection /* limit */ limit /* number */ 100 + /* in */ in /* role */ role /* first role */ foo /* comma */, + /* second role */ bar + /* admin */ admin /* admin role */ current_user/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_language.snap b/crates/squawk_fmt/tests/after/create_language.snap new file mode 100644 index 00000000..0424ad88 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_language.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_language.sql +--- +create language plsample; + +create or replace trusted procedural language + extraordinarily_long_procedural_language_name + handler extraordinarily_long_schema_name.extraordinarily_long_handler_function_name + inline extraordinarily_long_schema_name.extraordinarily_long_inline_function_name + validator extraordinarily_long_schema_name.extraordinarily_long_validator_function_name; + +-- comments in every position +create /* or */ or /* replace */ replace /* trusted */ trusted /* procedural */ procedural /* language */ language + /* name */ commented_language + /* handler */ handler /* handler schema */ handlers /* dot */./* handler function */ call_handler + /* inline */ inline /* inline function */ handlers.call_inline + /* validator */ validator /* validator function */ handlers.call_validator/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_materialized_view.snap b/crates/squawk_fmt/tests/after/create_materialized_view.snap new file mode 100644 index 00000000..a2a9db69 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_materialized_view.snap @@ -0,0 +1,38 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_materialized_view.sql +--- +create materialized view active_accounts as + select account_id, account_name from accounts where active = true; + +create materialized view if not exists + extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name ( + extraordinarily_long_account_identifier, + extraordinarily_long_account_display_name + ) + using extraordinarily_long_access_method_name + with (fillfactor = 80, autovacuum_enabled = true) + tablespace extraordinarily_long_tablespace_name + as + select + extraordinarily_long_account_identifier, + extraordinarily_long_account_display_name + from extraordinarily_long_schema_name.extraordinarily_long_accounts_table + with no data; + +-- comments in every position +create /* materialized */ materialized /* view */ view /* if */ if /* not */ not /* exists */ exists + /* schema */ app /* dot */./* name */ account_rollup /* columns */ ( + /* first column */ account_id /* comma */, + /* second column */ total /* close columns */ + ) + /* using */ using /* method */ heap + /* with */ with /* attributes */ ( + /* attribute */ fillfactor /* equals */ = /* value */ 90 /* close attributes */ + ) + /* tablespace */ tablespace /* tablespace name */ fastspace + /* as */ as + /* query */ select /* expression */ account_id, sum(amount) + from payments + group by account_id + /* with */ with /* no */ no /* data */ data/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_operator.snap b/crates/squawk_fmt/tests/after/create_operator.snap new file mode 100644 index 00000000..d73ce2b9 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_operator.snap @@ -0,0 +1,23 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_operator.sql +--- +create operator > ( + function = greater_than +); + +create operator extraordinarily_long_schema_name.## ( + function = extraordinarily_long_schema_name.extraordinarily_long_operator_function_name, + leftarg = extraordinarily_long_schema_name.extraordinarily_long_left_argument_type, + rightarg = extraordinarily_long_schema_name.extraordinarily_long_right_argument_type, + commutator = &&&&, + hashes, + merges +); + +-- comments in every position +create /* operator */ operator /* schema */ app /* dot */./* symbol */ # /* attributes */ ( + /* function name */ function /* equals */ = /* function value */ app.compare_values /* comma */, + /* left argument */ leftarg /* second equals */ = /* type */ integer /* comma */, + /* marker */ hashes /* closing parenthesis */ +)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_operator_class.snap b/crates/squawk_fmt/tests/after/create_operator_class.snap new file mode 100644 index 00000000..38a3ad01 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_operator_class.snap @@ -0,0 +1,49 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_operator_class.sql +--- +create operator class integer_minmax_ops + default for type integer + using btree as + operator 1 < for search, + function 1 (integer, integer) int4cmp(integer, integer), + storage integer; + +create operator class extraordinarily_long_schema_name.extraordinarily_long_integer_operator_class + default for type extraordinarily_long_schema_name.extraordinarily_long_integer_type + using extraordinarily_long_access_method + family extraordinarily_long_schema_name.extraordinarily_long_operator_family as + operator 1 < ( + extraordinarily_long_schema_name.extraordinarily_long_integer_type, + extraordinarily_long_schema_name.extraordinarily_long_integer_type + ) + for order by extraordinarily_long_schema_name.extraordinarily_long_sort_family, + function 1 ( + extraordinarily_long_schema_name.extraordinarily_long_integer_type, + extraordinarily_long_schema_name.extraordinarily_long_integer_type + ) + extraordinarily_long_schema_name.extraordinarily_long_comparison_function( + extraordinarily_long_schema_name.extraordinarily_long_integer_type, + extraordinarily_long_schema_name.extraordinarily_long_integer_type + ), + storage extraordinarily_long_schema_name.extraordinarily_long_storage_type; + +-- comments in every position +create /* operator */ operator /* class */ class /* schema */ app /* dot */./* name */ integer_ops + /* default */ default /* for */ for /* type keyword */ type /* data type */ integer + /* using */ using /* method */ btree + /* family */ family /* family schema */ app /* family dot */./* family name */ integer_family /* as */ as + /* option */ operator /* number */ 1 /* operator name */ < /* left paren */( + /* left type */ integer /* type comma */, + /* right type */ integer /* right paren */ + ) + /* purpose for */ for /* purpose search */ search /* option comma */, + /* function */ function /* function number */ 1 /* params */ ( + /* first param */ integer /* param comma */, + /* second param */ integer /* params close */ + ) + /* function signature */ app.compare_integers( + /* signature arg */ integer, + integer + ) /* second comma */, + /* storage */ storage /* storage type */ integer/* end */; diff --git a/crates/squawk_fmt/tests/after/create_operator_family.snap b/crates/squawk_fmt/tests/after/create_operator_family.snap new file mode 100644 index 00000000..d0359c84 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_operator_family.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_operator_family.sql +--- +create operator family integer_ops + using btree; + +create operator family + extraordinarily_long_schema_name.extraordinarily_long_operator_family_name + using extraordinarily_long_custom_index_access_method; + +-- comments in every position +create /* operator */ operator /* family */ family + /* schema */ app /* dot */./* name */ integer_family + /* using */ using /* method */ btree/* end */; diff --git a/crates/squawk_fmt/tests/after/create_policy.snap b/crates/squawk_fmt/tests/after/create_policy.snap new file mode 100644 index 00000000..0b015271 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_policy.snap @@ -0,0 +1,30 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_policy.sql +--- +create policy account_access on public.accounts; + +create policy extraordinarily_long_account_access_policy_name + on extraordinarily_long_schema_name.extraordinarily_long_accounts_table_name + as restrictive + for select + to extraordinarily_long_account_administrator_role, + extraordinarily_long_account_auditor_role + using ( + account_owner_identifier = "current_user" and account_identifier > 1000000 + ) + with check (account_is_active and account_owner_identifier = "current_user"); + +-- comments in every position +create /* policy */ policy /* name */ account_access + /* on */ on /* schema */ public /* dot */./* table */ accounts + /* as */ as /* policy type */ restrictive + /* for */ for /* command */ select + /* to */ to /* first role */ account_admin /* comma */, + /* second role */ account_auditor + /* using */ using /* left paren */ ( + /* expression */ owner_name = "current_user" /* right paren */ + ) + /* with */ with /* check */ check /* check left paren */ ( + /* check expression */ active /* check right paren */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/create_procedure.snap b/crates/squawk_fmt/tests/after/create_procedure.snap new file mode 100644 index 00000000..10b3ea2e --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_procedure.snap @@ -0,0 +1,27 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_procedure.sql +--- +create procedure refresh_accounts() + language sql + as $$ select 1 $$; + +create or replace procedure extraordinarily_long_schema_name.extraordinarily_long_account_recalculation_procedure( + in extraordinarily_long_account_identifier integer, + in extraordinarily_long_effective_timestamp timestamp with time zone, + out extraordinarily_long_recalculated_total numeric +) + language plpgsql + security definer + set work_mem = '256MB' + as $$ begin null; end $$; + +-- comments in every position +create /* or */ or /* replace */ replace /* procedure */ procedure /* schema */ app /* dot */./* name */ recalculate/* params */ ( + /* mode */ in /* parameter */ account_id /* type */ integer /* comma */, + /* out mode */ out /* output */ result /* output type */ text /* close */ +) + /* language */ language /* language name */ plpgsql + /* security */ security /* definer */ definer + /* set */ set /* parameter name */ work_mem /* equals */ = /* setting */ '64MB' + /* as */ as /* body */ $$ begin null; end $$/* end */; diff --git a/crates/squawk_fmt/tests/after/create_property_graph.snap b/crates/squawk_fmt/tests/after/create_property_graph.snap new file mode 100644 index 00000000..6ba48288 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_property_graph.snap @@ -0,0 +1,64 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_property_graph.sql +--- +create property graph social + vertex tables (people key (id)) + edge tables ( + follows + source key (follower_id) references people (id) + destination key (followed_id) references people (id) + ); + +create unlogged property graph extraordinarily_long_social_relationship_property_graph + vertex tables ( + extraordinarily_long_people_table key ( + extraordinarily_long_person_identifier + ) + label person properties (display_name as public_name, date_of_birth), + extraordinarily_long_organizations_table key ( + extraordinarily_long_organization_identifier + ) + no properties + ) + edge tables ( + extraordinarily_long_employment_relationship_table + source key ( + employee_identifier + ) references extraordinarily_long_people_table ( + extraordinarily_long_person_identifier + ) + destination key ( + employer_identifier + ) references extraordinarily_long_organizations_table ( + extraordinarily_long_organization_identifier + ) + properties all columns + ); + +-- comments in every position +create /* persistence */ local /* temporary */ temporary /* property */ property /* graph */ graph /* graph name */ public /* dot */./* name */ social + /* vertex */ vertex /* tables */ tables /* left paren */( + /* table */ people /* key */ key /* key left */ ( + /* key column */ id /* key right */ + ) + /* label */ label /* label name */ person /* properties */ properties /* properties left */ ( + /* expression */ full_name /* as */ as /* property name */ name /* properties right */ + ) /* comma */, + /* second table */ organizations + /* no */ no /* properties */ properties /* vertex right */ + ) + /* edge */ edge /* edge tables */ tables /* edge left */( + /* edge table */ follows + /* source */ source /* source key */ key /* source left */ ( + /* source column */ follower_id /* source right */ + ) /* references */ references /* source table */ people /* source reference left */ ( + /* source reference column */ id /* source reference right */ + ) + /* destination */ destination /* destination key */ key /* destination left */ ( + /* destination column */ followed_id /* destination right */ + ) /* destination references */ references /* destination table */ people /* destination reference left */ ( + /* destination reference column */ id /* destination reference right */ + ) + /* all properties */ properties /* all */ all /* columns */ columns /* edge right */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/create_publication.snap b/crates/squawk_fmt/tests/after/create_publication.snap index 40a46467..5db240a8 100644 --- a/crates/squawk_fmt/tests/after/create_publication.snap +++ b/crates/squawk_fmt/tests/after/create_publication.snap @@ -3,31 +3,34 @@ source: crates/squawk_fmt/tests/tests.rs input_file: crates/squawk_fmt/tests/before/create_publication.sql --- create publication everything -for all tables, all sequences except (table audit.secret_events, internal_jobs) -with ( - publish = 'insert, update, delete, truncate', - publish_via_partition_root = true -); + for + all tables, + all sequences + except (table audit.secret_events, internal_jobs) + with ( + publish = 'insert, update, delete, truncate', + publish_via_partition_root = true + ); create publication selected_tables -for - table only public.accounts (id, email) where (id > 100), - table (public.orders)*, - tables in schema reporting where (tenant_id = 42), - current_schema; + for + table only public.accounts (id, email) where (id > 100), + table (public.orders)*, + tables in schema reporting where (tenant_id = 42), + current_schema; create publication no_tables with (publish = 'insert'); create /* after create */ publication /* after publication */ commented_pub -for - /* after for */ table /* after table */ only /* after only */(/* before table name */ public.commented /* before close */)/* before star */ * /* before columns */ ( - /* before column */ id /* before comma */, - /* after comma */ payload /* before columns close */ - ) - /* before where */ where /* before where open */( - /* before expression */ id > 0 /* before where close */ - ), - /* after object comma */ tables /* after tables */ in /* after in */ schema /* after schema */ current_schema -with /* after with */ ( - /* before option */ publish /* before equals */ = /* before value */ 'insert' /* before options close */ -)/* before semicolon */; + for + /* after for */ table /* after table */ only /* after only */(/* before table name */ public.commented /* before close */)/* before star */ * /* before columns */ ( + /* before column */ id /* before comma */, + /* after comma */ payload /* before columns close */ + ) + /* before where */ where /* before where open */ ( + /* before expression */ id > 0 /* before where close */ + ), + /* after object comma */ tables /* after tables */ in /* after in */ schema /* after schema */ current_schema + with /* after with */ ( + /* before option */ publish /* before equals */ = /* before value */ 'insert' /* before options close */ + )/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_role.snap b/crates/squawk_fmt/tests/after/create_role.snap new file mode 100644 index 00000000..0ecb1e41 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_role.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_role.sql +--- +create role analyst; + +create role extraordinarily_long_reporting_and_data_warehouse_administrator + with login + encrypted password 'an-extraordinarily-long-secret-value' + valid until '2035-12-31 23:59:59+00' + connection limit 25 + in role extraordinarily_long_read_only_reporting_role, + extraordinarily_long_data_warehouse_role + admin database_administrator; + +-- comments in every position +create /* role */ role /* name */ reporting_user + /* with */ with /* login */ login + /* encrypted */ encrypted /* password */ password /* secret */ 'secret' + /* valid */ valid /* until */ until /* timestamp */ '2030-01-01' + /* connection */ connection /* limit */ limit /* count */ 10 + /* in */ in /* role option */ role /* first role */ readers /* comma */, + /* second role */ writers + /* admin */ admin /* admin role */ role_admin/* end */; diff --git a/crates/squawk_fmt/tests/after/create_rule.snap b/crates/squawk_fmt/tests/after/create_rule.snap new file mode 100644 index 00000000..d8bb02a2 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_rule.snap @@ -0,0 +1,39 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_rule.sql +--- +create rule ignore_delete as on delete + to public.accounts + do instead nothing; + +create or replace rule extraordinarily_long_rule_name_for_recording_all_account_changes + as on update + to extraordinarily_long_schema_name.extraordinarily_long_accounts_table_name + where ( + old.account_owner_identifier is distinct from new.account_owner_identifier + and new.account_is_active + ) + do also ( + notify extraordinarily_long_account_changes_channel, + 'account owner changed'; + update extraordinarily_long_audit_table_name + set account_was_changed = true + where account_identifier = new.account_identifier; + ); + +-- comments in every position +create /* or */ or /* replace */ replace /* rule */ rule /* name */ audit_change + /* as */ as /* on */ on /* event */ update + /* to */ to /* schema */ public /* dot */./* relation */ accounts + /* where */ where /* left paren */ ( + /* condition */ old.owner_id + is distinct from new.owner_id /* right paren */ + ) + /* do */ do /* instead */ instead /* action left */ ( + /* notify */ notify /* channel */ account_changes /* comma */, + /* payload */ 'changed'/* first semicolon */; + /* update */ update /* table */ audit_log + /* set */ set + /* column */ changed + /* equals */ = /* value */ true/* second semicolon */; /* action right */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/create_schema.snap b/crates/squawk_fmt/tests/after/create_schema.snap new file mode 100644 index 00000000..52dd7c84 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_schema.snap @@ -0,0 +1,19 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_schema.sql +--- +create schema app; + +create schema if not exists + extraordinarily_long_application_reporting_and_data_warehouse_schema + authorization extraordinarily_long_application_reporting_and_data_warehouse_owner_role; + +create schema authorization "current_user"; + +-- comments in every position +create /* schema */ schema /* if */ if /* not */ not /* exists */ exists + /* schema name */ reporting + /* authorization */ authorization /* role */ reporting_owner/* end */; + +create /* schema */ schema + /* authorization */ authorization /* role */ schema_owner/* end */; diff --git a/crates/squawk_fmt/tests/after/create_sequence.snap b/crates/squawk_fmt/tests/after/create_sequence.snap new file mode 100644 index 00000000..2fec1cb9 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_sequence.snap @@ -0,0 +1,28 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_sequence.sql +--- +create sequence invoice_number_seq; + +create temporary sequence if not exists + extraordinarily_long_schema_name.extraordinarily_long_invoice_number_sequence + as bigint + increment by 5 + minvalue 100 + maxvalue 999999999 + start with 100 + cache 50 + cycle + owned by extraordinarily_long_schema_name.extraordinarily_long_table_name.extraordinarily_long_column_name; + +-- comments in every position +create /* temporary */ temporary /* sequence */ sequence /* if */ if /* not */ not /* exists */ exists + /* name */ app.order_seq + /* as */ as /* type */ bigint + /* increment */ increment /* by */ by /* increment value */ 2 + /* no */ no /* minvalue */ minvalue + /* maxvalue */ maxvalue /* max */ 1000 + /* start */ start /* with */ with /* start value */ 10 + /* cache */ cache /* cache value */ 20 + /* no cycle */ no /* cycle */ cycle + /* owned */ owned /* by */ by /* owner */ app.orders.id/* end */; diff --git a/crates/squawk_fmt/tests/after/create_server.snap b/crates/squawk_fmt/tests/after/create_server.snap new file mode 100644 index 00000000..290daa21 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_server.snap @@ -0,0 +1,27 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_server.sql +--- +create server analytics foreign data wrapper postgres_fdw; + +create server if not exists + extraordinarily_long_analytics_foreign_server + type 'extraordinarily long server type description' + version 'PostgreSQL version 17.2 with additional compatibility information' + foreign data wrapper extraordinarily_long_foreign_data_wrapper_name + options ( + host 'analytics.example.internal', + dbname 'extraordinarily_long_analytics_database_name', + port '5432' + ); + +-- comments in every position +create /* server */ server /* if */ if /* not */ not /* exists */ exists + /* name */ reporting + /* type */ type /* type value */ 'postgresql' + /* version */ version /* version value */ '17' + /* foreign */ foreign /* data */ data /* wrapper */ wrapper /* fdw */ postgres_fdw + /* options */ options /* open */ ( + /* option name */ host /* option value */ 'localhost' /* comma */, + /* second name */ port /* second value */ '5432' /* close */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/create_statistics.snap b/crates/squawk_fmt/tests/after/create_statistics.snap new file mode 100644 index 00000000..39cf6006 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_statistics.snap @@ -0,0 +1,31 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_statistics.sql +--- +create statistics user_activity_stats + on user_id, created_at + from app.user_activity; + +create statistics if not exists + extraordinarily_long_schema_name.extraordinarily_long_statistics_name ( + ndistinct, + dependencies, + mcv + ) + on + extraordinarily_long_first_column_name, + (lower(extraordinarily_long_second_column_name)), + extraordinarily_long_third_column_name + + extraordinarily_long_fourth_column_name + from extraordinarily_long_schema_name.extraordinarily_long_table_name; + +-- comments in every position +create /* statistics */ statistics /* if */ if /* not */ not /* exists */ exists + /* name */ app.orders_stats /* open kinds */ ( + /* first kind */ ndistinct /* kind comma */, + /* second kind */ dependencies /* close kinds */ + ) + /* on */ on + /* first expression */ customer_id /* expression comma */, + /* second expression */ (lower(email)) + /* from */ from /* table */ app.orders/* end */; diff --git a/crates/squawk_fmt/tests/after/create_subscription.snap b/crates/squawk_fmt/tests/after/create_subscription.snap index 21628651..b619836e 100644 --- a/crates/squawk_fmt/tests/after/create_subscription.snap +++ b/crates/squawk_fmt/tests/after/create_subscription.snap @@ -3,15 +3,15 @@ source: crates/squawk_fmt/tests/tests.rs input_file: crates/squawk_fmt/tests/before/create_subscription.sql --- create subscription local_sub -connection 'host=localhost port=5432 dbname=publisher user=replicator password=very_long_password' -publication all_changes, - selected_tables -with (copy_data = true, enabled = false, streaming = parallel); + connection 'host=localhost port=5432 dbname=publisher user=replicator password=very_long_password' + publication all_changes, + selected_tables + with (copy_data = true, enabled = false, streaming = parallel); create subscription server_sub server publisher_server publication all_changes; create /* after create */ subscription /* before name */ commented_sub -connection /* before connection */ 'host=localhost' -publication /* before publication */ all_changes, - /* after comma */ selected_tables -with /* before params */ (enabled = true)/* before semicolon */; + connection /* before connection */ 'host=localhost' + publication /* before publication */ all_changes, + /* after comma */ selected_tables + with /* before params */ (enabled = true)/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_table.snap b/crates/squawk_fmt/tests/after/create_table.snap index 24c7addc..3b41fc71 100644 --- a/crates/squawk_fmt/tests/after/create_table.snap +++ b/crates/squawk_fmt/tests/after/create_table.snap @@ -118,7 +118,7 @@ create table column_option_comments ( /*c*/ storage /*d*/ external /*e*/ compression /*f*/ lz4 /*g*/ with /*h*/ options - /*i*/ options /*j*/( + /*i*/ options /*j*/ ( /*k*/ add /*l*/ formatter /*m*/ 'x' /*n*/, /*o*/ set /*p*/ formatter /*q*/ 'y' /*r*/, /*s*/ drop /*t*/ formatter /*u*/ diff --git a/crates/squawk_fmt/tests/after/create_tablespace.snap b/crates/squawk_fmt/tests/after/create_tablespace.snap new file mode 100644 index 00000000..b4444d6b --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_tablespace.snap @@ -0,0 +1,24 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_tablespace.sql +--- +create tablespace fastspace + location '/data/fastspace'; + +create tablespace extraordinarily_long_tablespace_name_for_historical_analytics + owner extraordinarily_long_database_owner_role + location '/srv/postgresql/extraordinarily/long/location/for/historical/analytics/tablespace' + with ( + random_page_cost = 1.1, + seq_page_cost = 0.9, + effective_io_concurrency = 200 + ); + +-- comments in every position +create /* tablespace */ tablespace /* name */ reporting_space + /* owner */ owner /* role */ reporting_owner + /* location */ location /* path */ '/srv/reporting' + /* with */ with /* open */ ( + /* first name */ random_page_cost /* equals */ = /* first value */ 1.2 /* comma */, + /* second name */ seq_page_cost /* second equals */ = /* second value */ 1.0 /* close */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/create_text_search_configuration.snap b/crates/squawk_fmt/tests/after/create_text_search_configuration.snap new file mode 100644 index 00000000..28028981 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_text_search_configuration.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_text_search_configuration.sql +--- +create text search configuration public.english ( + parser = public.default_parser +); + +create text search configuration + extraordinarily_long_schema_name.extraordinarily_long_configuration_name ( + copy = public.extraordinarily_long_source_configuration_name +); + +create /* text keyword */ text /* search keyword */ search /* configuration keyword */ configuration + /* name */ public /* dot */./* name segment */ commented_configuration /* left parenthesis */ ( + /* first option */ parser /* equals */ = /* value */ public.default_parser /* comma */, + /* second option */ copy = /* second value */ public.english /* right parenthesis */ +)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_text_search_dictionary.snap b/crates/squawk_fmt/tests/after/create_text_search_dictionary.snap new file mode 100644 index 00000000..35b44ec0 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_text_search_dictionary.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_text_search_dictionary.sql +--- +create text search dictionary public.english_stem ( + template = snowball, + language = english +); + +create text search dictionary extraordinarily_long_schema_name.extraordinarily_long_dictionary_name ( + template = public.extraordinarily_long_dictionary_template_name, + stopwords = 'extraordinarily_long_stopword_dictionary_name' +); + +create /* text keyword */ text /* search keyword */ search /* dictionary keyword */ dictionary /* name */ public /* dot */./* name segment */ commented_dictionary /* left parenthesis */ ( + /* first option */ template /* equals */ = /* value */ public.snowball /* comma */, + /* second option */ language = /* second value */ english /* right parenthesis */ +)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_text_search_parser.snap b/crates/squawk_fmt/tests/after/create_text_search_parser.snap new file mode 100644 index 00000000..27d48d5c --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_text_search_parser.snap @@ -0,0 +1,25 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_text_search_parser.sql +--- +create text search parser public.default_parser ( + start = public.parser_start, + gettoken = public.parser_gettoken, + "end" = public.parser_end, + lextypes = public.parser_lextypes +); + +create text search parser extraordinarily_long_schema_name.extraordinarily_long_parser_name ( + start = public.extraordinarily_long_parser_start_function_name, + gettoken = public.extraordinarily_long_parser_gettoken_function_name, + "end" = public.extraordinarily_long_parser_end_function_name, + lextypes = public.extraordinarily_long_parser_lextypes_function_name, + headline = public.extraordinarily_long_parser_headline_function_name +); + +create /* text keyword */ text /* search keyword */ search /* parser keyword */ parser /* name */ public /* dot */./* name segment */ commented_parser /* left parenthesis */ ( + /* first option */ start /* equals */ = /* value */ public.parser_start /* comma */, + /* second option */ gettoken = /* second value */ public.parser_gettoken /* comma two */, + /* third option */ "end" = public.parser_end, + lextypes = public.parser_lextypes /* right parenthesis */ +)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_text_search_template.snap b/crates/squawk_fmt/tests/after/create_text_search_template.snap new file mode 100644 index 00000000..f51fb40d --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_text_search_template.snap @@ -0,0 +1,17 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_text_search_template.sql +--- +create text search template public.simple_template ( + lexize = public.simple_lexize +); + +create text search template extraordinarily_long_schema_name.extraordinarily_long_template_name ( + init = public.extraordinarily_long_template_initialization_function_name, + lexize = public.extraordinarily_long_template_lexize_function_name +); + +create /* text keyword */ text /* search keyword */ search /* template keyword */ template /* name */ public /* dot */./* name segment */ commented_template /* left parenthesis */ ( + /* first option */ init /* equals */ = /* value */ public.simple_init /* comma */, + /* second option */ lexize = /* second value */ public.simple_lexize /* right parenthesis */ +)/* semicolon */; diff --git a/crates/squawk_fmt/tests/after/create_type.snap b/crates/squawk_fmt/tests/after/create_type.snap new file mode 100644 index 00000000..16dc1c67 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_type.snap @@ -0,0 +1,51 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_type.sql +--- +create type shell_type; + +create type inventory_item as ( + name text, + supplier_id integer, + extraordinarily_long_product_description character varying(255) collate "C", + price numeric +); + +create type mood as enum ( + 'sad', + 'ok', + 'happy', + 'extraordinarily delighted and enthusiastic' +); + +create type float8_range as range (subtype = float8, subtype_diff = float8mi); + +create type complex_base ( + input = complex_in, + output = complex_out, + internallength = 16 +); + +-- comments in every composite type position +create /* type */ type /* name */ commented_composite /* as */ as /* open */ ( + /* field */ first_field /* type */ text + /* collate */ collate /* collation */ "C" /* comma */, + /* next field */ second_field /* next type */ integer /* close */ +)/* end */; + +-- comments in every enum type position +create type /* enum name */ commented_enum /* as */ as /* enum */ enum /* open */ ( + /* first */ 'one' /* comma */, + /* second */ 'two' /* close */ +)/* end */; + +-- comments in every range/base type position +create type /* range name */ commented_range /* as */ as /* range */ range /* attributes */ ( + /* option */ subtype /* equals */ = /* value */ numeric /* comma */, + /* option two */ multirange_type_name = commented_multirange /* close */ +)/* end */; + +create type /* base name */ commented_base /* attributes */ ( + /* input */ input /* equals */ = /* value */ commented_in /* comma */, + /* output */ output = commented_out /* close */ +)/* end */; diff --git a/crates/squawk_fmt/tests/after/create_user.snap b/crates/squawk_fmt/tests/after/create_user.snap new file mode 100644 index 00000000..f67f7fa6 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_user.snap @@ -0,0 +1,22 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_user.sql +--- +create user app_user; + +create user extraordinarily_long_application_reporting_user + with login + encrypted password 'an-extraordinarily-long-secret-value' + valid until '2035-12-31 23:59:59+00' + connection limit 25 + in role extraordinarily_long_read_only_reporting_role, + extraordinarily_long_data_warehouse_role; + +-- comments in every position +create /* user */ user /* name */ reporting_user + /* with */ with /* login */ login + /* encrypted */ encrypted /* password */ password /* secret */ 'secret' + /* valid */ valid /* until */ until /* timestamp */ '2030-01-01' + /* connection */ connection /* limit */ limit /* count */ 10 + /* in */ in /* role option */ role /* first role */ readers /* comma */, + /* second role */ writers/* end */; diff --git a/crates/squawk_fmt/tests/after/create_user_mapping.snap b/crates/squawk_fmt/tests/after/create_user_mapping.snap new file mode 100644 index 00000000..96070cd5 --- /dev/null +++ b/crates/squawk_fmt/tests/after/create_user_mapping.snap @@ -0,0 +1,20 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/create_user_mapping.sql +--- +create user mapping for app_user server app_server; + +create user mapping if not exists for extraordinarily_long_application_reporting_user + server extraordinarily_long_foreign_data_server + options ( + "user" 'extraordinarily_long_remote_user_name', + password 'an-extraordinarily-long-secret-value' + ); + +-- comments in every position +create /* user */ user /* mapping */ mapping /* if */ if /* not */ not /* exists */ exists /* for */ for /* role */ current_user + /* server */ server /* server name */ remote_server + /* options */ options /* open */ ( + /* option */ "user" /* value */ 'remote_user' /* comma */, + /* next option */ password /* next value */ 'secret' /* close */ + )/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_access_method.snap b/crates/squawk_fmt/tests/after/drop_access_method.snap new file mode 100644 index 00000000..6b1d2e38 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_access_method.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_access_method.sql +--- +drop access method heap2; + +drop access method if exists + extraordinarily_long_custom_table_access_method_name + cascade; + +-- comments in every position +drop /* access */ access /* method */ method /* if */ if /* exists */ exists + /* name */ custom_method + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_aggregate.snap b/crates/squawk_fmt/tests/after/drop_aggregate.snap new file mode 100644 index 00000000..e57b5743 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_aggregate.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_aggregate.sql +--- +drop aggregate public.sum(integer); + +drop aggregate if exists + extraordinarily_long_schema_name.extraordinarily_long_aggregate_name( + extraordinarily_long_schema_name.extraordinarily_long_argument_type_name + ), + public.another_extraordinarily_long_aggregate_name(double precision, numeric) + cascade; + +drop aggregate + public.ordered_set_aggregate(double precision order by anyelement, text); + +-- comments in every position +drop /* aggregate */ aggregate /* if */ if /* exists */ exists + /* first aggregate */ public.first_aggregate( + /* first argument */ integer /* first close */ + ) /* before comma */, + /* second aggregate */ public.second_aggregate( + /* second argument */ text, + /* third argument */ numeric /* second close */ + ) + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_cast.snap b/crates/squawk_fmt/tests/after/drop_cast.snap new file mode 100644 index 00000000..84d8c71d --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_cast.snap @@ -0,0 +1,19 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +assertion_line: 28 +input_file: crates/squawk_fmt/tests/before/drop_cast.sql +--- +drop cast (integer as bigint); + +drop cast if exists ( + extraordinarily_long_schema_name.extraordinarily_long_source_type_name + as + extraordinarily_long_schema_name.extraordinarily_long_target_type_name +) cascade; + +-- comments in every position +drop /* cast */ cast /* if */ if /* exists */ exists /* signature */ ( + /* source type */ text + /* as */ as + /* target type */ varchar + /* closing parenthesis */) /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_collation.snap b/crates/squawk_fmt/tests/after/drop_collation.snap new file mode 100644 index 00000000..e23e33a6 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_collation.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_collation.sql +--- +drop collation public.english; + +drop collation if exists + extraordinarily_long_schema_name.extraordinarily_long_collation_name, + another_extraordinarily_long_schema_name.another_extraordinarily_long_collation_name + cascade; + +-- comments in every position +drop /* collation */ collation /* if */ if /* exists */ exists + /* first collation */ public.english /* before comma */, + /* second collation */ public.french + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_conversion.snap b/crates/squawk_fmt/tests/after/drop_conversion.snap new file mode 100644 index 00000000..6250d958 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_conversion.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_conversion.sql +--- +drop conversion public.my_conversion; + +drop conversion if exists + extraordinarily_long_schema_name.extraordinarily_long_character_set_conversion_name + cascade; + +-- comments in every position +drop /* conversion */ conversion /* if */ if /* exists */ exists + /* conversion name */ public.commented_conversion + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_database.snap b/crates/squawk_fmt/tests/after/drop_database.snap new file mode 100644 index 00000000..039fb5ed --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_database.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_database.sql +--- +drop database application; + +drop database reporting (force); + +drop database if exists + extraordinarily_long_database_name_used_to_verify_statement_line_wrapping +with (force); + +-- comments in every position +drop /* database */ database /* if */ if /* exists */ exists + /* name */ application +/* with */ with /* left paren */ (/* force */ force /* right paren */)/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_domain.snap b/crates/squawk_fmt/tests/after/drop_domain.snap new file mode 100644 index 00000000..7ef03a2c --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_domain.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_domain.sql +--- +drop domain public.email_address; + +drop domain if exists + extraordinarily_long_schema_name.extraordinarily_long_domain_name, + another_extraordinarily_long_schema_name.another_extraordinarily_long_domain_name + cascade; + +-- comments in every position +drop /* domain */ domain /* if */ if /* exists */ exists + /* first domain */ public.email_address /* before comma */, + /* second domain */ public.postal_code + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_event_trigger.snap b/crates/squawk_fmt/tests/after/drop_event_trigger.snap new file mode 100644 index 00000000..2d877648 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_event_trigger.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_event_trigger.sql +--- +drop event trigger ddl_audit; + +drop event trigger if exists + extraordinarily_long_event_trigger_name_used_to_verify_statement_line_wrapping + cascade; + +-- comments in every position +drop /* event */ event /* trigger */ trigger /* if */ if /* exists */ exists + /* name */ ddl_audit + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_extension.snap b/crates/squawk_fmt/tests/after/drop_extension.snap new file mode 100644 index 00000000..ed32b7fe --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_extension.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_extension.sql +--- +drop extension hstore; + +drop extension if exists + extraordinarily_long_extension_name_used_to_verify_statement_line_wrapping, + another_extraordinarily_long_extension_name + cascade; + +-- comments in every position +drop /* extension */ extension /* if */ if /* exists */ exists + /* first extension */ hstore /* before comma */, + /* second extension */ pg_trgm + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_foreign_data_wrapper.snap b/crates/squawk_fmt/tests/after/drop_foreign_data_wrapper.snap new file mode 100644 index 00000000..bfb0ff97 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_foreign_data_wrapper.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_foreign_data_wrapper.sql +--- +drop foreign data wrapper analytics_fdw; + +drop foreign data wrapper if exists + extraordinarily_long_foreign_data_wrapper_name_for_analytics, + another_extraordinarily_long_foreign_data_wrapper_name + cascade; + +-- comments in every position +drop /* foreign */ foreign /* data */ data /* wrapper */ wrapper /* if */ if /* exists */ exists + /* first wrapper */ analytics_fdw /* before comma */, + /* second wrapper */ reporting_fdw + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_foreign_table.snap b/crates/squawk_fmt/tests/after/drop_foreign_table.snap new file mode 100644 index 00000000..702e2469 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_foreign_table.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_foreign_table.sql +--- +drop foreign table public.events; + +drop foreign table if exists + extraordinarily_long_schema_name.extraordinarily_long_foreign_table_name, + another_extraordinarily_long_schema_name.another_extraordinarily_long_foreign_table_name + cascade; + +-- comments in every position +drop /* foreign */ foreign /* table */ table /* if */ if /* exists */ exists + /* first table */ public /* dot */.events /* before comma */, + /* second table */ reporting.archive + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_function.snap b/crates/squawk_fmt/tests/after/drop_function.snap new file mode 100644 index 00000000..325fe176 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_function.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_function.sql +--- +drop function public.calculate_total(integer, numeric); + +drop function if exists + extraordinarily_long_schema_name.extraordinarily_long_function_name( + extraordinarily_long_schema_name.extraordinarily_long_argument_type_name, + double precision + ), + public.another_extraordinarily_long_function_name( + integer + ) + cascade; + +-- comments in every position +drop /* function */ function /* if */ if /* exists */ exists + /* first function */ public /* dot */.calculate_total /* open */( + /* first argument */ integer /* argument comma */, + /* second argument */ numeric /* close */ + ) /* function comma */, + /* second function */ reporting.refresh_cache( + /* second function argument */ text + ) + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_group.snap b/crates/squawk_fmt/tests/after/drop_group.snap new file mode 100644 index 00000000..de315a0a --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_group.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_group.sql +--- +drop group analysts; + +drop group if exists + extraordinarily_long_group_name_for_application_analytics, + another_extraordinarily_long_group_name_for_application_reporting; + +-- comments in every position +drop /* group */ group /* if */ if /* exists */ exists + /* first group */ analysts /* before comma */, + /* second group */ reporting_users/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_index.snap b/crates/squawk_fmt/tests/after/drop_index.snap new file mode 100644 index 00000000..bde870de --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_index.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_index.sql +--- +drop index public.events_created_at_idx; + +drop index concurrently if exists + extraordinarily_long_schema_name.extraordinarily_long_index_name, + another_extraordinarily_long_schema_name.another_extraordinarily_long_index_name + cascade; + +-- comments in every position +drop /* index */ index /* concurrently */ concurrently /* if */ if /* exists */ exists + /* first index */ public /* dot */.events_idx /* before comma */, + /* second index */ reporting.archive_idx + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_language.snap b/crates/squawk_fmt/tests/after/drop_language.snap new file mode 100644 index 00000000..21d64a74 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_language.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_language.sql +--- +drop language plpgsql; + +drop procedural language if exists + extraordinarily_long_procedural_language_name + cascade; + +-- comments in every position +drop /* procedural */ procedural /* language */ language /* if */ if /* exists */ exists + /* language name */ old_language + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_materialized_view.snap b/crates/squawk_fmt/tests/after/drop_materialized_view.snap new file mode 100644 index 00000000..e28fe42a --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_materialized_view.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_materialized_view.sql +--- +drop materialized view public.monthly_sales; + +drop materialized view if exists + extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name, + another_extraordinarily_long_schema_name.another_extraordinarily_long_materialized_view_name + cascade; + +-- comments in every position +drop /* materialized */ materialized /* view */ view /* if */ if /* exists */ exists + /* first view */ public /* dot */.monthly_sales /* before comma */, + /* second view */ reporting.archived_sales + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_operator.snap b/crates/squawk_fmt/tests/after/drop_operator.snap new file mode 100644 index 00000000..a968d83a --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_operator.snap @@ -0,0 +1,25 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_operator.sql +--- +drop operator public.===(integer, integer); + +drop operator if exists + extraordinarily_long_schema_name.===( + extraordinarily_long_schema_name.extraordinarily_long_left_operand_type, + extraordinarily_long_schema_name.extraordinarily_long_right_operand_type + ), + public.<>(none, double precision) + cascade; + +-- comments in every position +drop /* operator */ operator /* if */ if /* exists */ exists + /* first operator */ public /* path dot */.=== /* open */( + /* left type */ integer /* type comma */, + /* right type */ integer /* close */ + ) /* signature comma */, + /* second operator */ ~ /* second open */( + /* none */ none /* second comma */, + /* second type */ text /* second close */ + ) + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_operator_class.snap b/crates/squawk_fmt/tests/after/drop_operator_class.snap new file mode 100644 index 00000000..95a2df37 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_operator_class.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_operator_class.sql +--- +drop operator class public.text_pattern_ops using btree; + +drop operator class if exists + extraordinarily_long_schema_name.extraordinarily_long_operator_class_name + using extraordinarily_long_access_method_name + cascade; + +-- comments in every position +drop /* operator */ operator /* class */ class /* if */ if /* exists */ exists + /* class name */ public /* dot */.text_pattern_ops + /* using */ using /* method */ btree + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_operator_family.snap b/crates/squawk_fmt/tests/after/drop_operator_family.snap new file mode 100644 index 00000000..0fc5d06a --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_operator_family.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_operator_family.sql +--- +drop operator family public.integer_ops using btree; + +drop operator family if exists + extraordinarily_long_schema_name.extraordinarily_long_operator_family_name + using extraordinarily_long_access_method_name + restrict; + +-- comments in every position +drop /* operator */ operator /* family */ family /* if */ if /* exists */ exists + /* family name */ public /* dot */.integer_ops + /* using */ using /* method */ hash + /* behavior */ cascade/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_owned.snap b/crates/squawk_fmt/tests/after/drop_owned.snap new file mode 100644 index 00000000..3d78125d --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_owned.snap @@ -0,0 +1,18 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_owned.sql +--- +drop owned by alice; + +drop owned by + extraordinarily_long_database_role_name, + another_extraordinarily_long_database_role_name, + current_user + cascade; + +-- comments in every position +drop /* owned */ owned /* by */ by + /* first role */ alice /* first comma */, + /* second role */ current_user /* second comma */, + /* group keyword */ group /* group name */ managers + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_policy.snap b/crates/squawk_fmt/tests/after/drop_policy.snap new file mode 100644 index 00000000..e0b39d37 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_policy.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_policy.sql +--- +drop policy account_policy on public.accounts; + +drop policy if exists + extraordinarily_long_policy_name_for_customer_account_management + on extraordinarily_long_schema_name.extraordinarily_long_customer_accounts_table + cascade; + +-- comments in every position +drop /* policy */ policy /* if */ if /* exists */ exists + /* policy name */ account_policy + /* on */ on /* table schema */ public /* dot */.accounts + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_procedure.snap b/crates/squawk_fmt/tests/after/drop_procedure.snap new file mode 100644 index 00000000..6af32bc4 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_procedure.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_procedure.sql +--- +drop procedure public.refresh_accounts(integer, text); + +drop procedure if exists + extraordinarily_long_schema_name.extraordinarily_long_account_refresh_procedure( + extraordinarily_long_schema_name.extraordinarily_long_account_identifier_type, + double precision + ), + public.another_extraordinarily_long_procedure_name( + integer + ) + cascade; + +-- comments in every position +drop /* procedure */ procedure /* if */ if /* exists */ exists + /* first procedure */ public /* dot */.refresh_accounts /* open */( + /* first argument */ integer /* argument comma */, + /* second argument */ text /* close */ + ) /* procedure comma */, + /* second procedure */ reporting.rebuild_cache( + /* argument */ bigint + ) + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_property_graph.snap b/crates/squawk_fmt/tests/after/drop_property_graph.snap new file mode 100644 index 00000000..f139e74f --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_property_graph.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_property_graph.sql +--- +drop property graph social_graph; + +drop property graph if exists + extraordinarily_long_schema_name.extraordinarily_long_social_relationship_property_graph + cascade; + +-- comments in every position +drop /* property */ property /* graph */ graph /* if */ if /* exists */ exists + /* schema */ public /* dot */./* graph name */ social_graph + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_publication.snap b/crates/squawk_fmt/tests/after/drop_publication.snap index af82f46f..2b96bf35 100644 --- a/crates/squawk_fmt/tests/after/drop_publication.snap +++ b/crates/squawk_fmt/tests/after/drop_publication.snap @@ -7,4 +7,4 @@ drop publication if exists all_changes, selected_tables cascade; drop /* after drop */ publication /* before if */ if /* before exists */ exists /* before name */ commented_pub, /* after comma */ selected_tables -/* before behavior */ restrict/* before semicolon */; + /* before behavior */ restrict/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/drop_role.snap b/crates/squawk_fmt/tests/after/drop_role.snap new file mode 100644 index 00000000..38589354 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_role.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_role.sql +--- +drop role analysts; + +drop role if exists + extraordinarily_long_role_name_for_application_analytics_and_business_intelligence, + another_extraordinarily_long_role_name_for_application_reporting; + +-- comments in every position +drop /* role */ role /* if */ if /* exists */ exists + /* first role */ analysts /* comma */, + /* second role */ current_user /* comma */, + /* group */ group /* group name */ reporting_users/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_routine.snap b/crates/squawk_fmt/tests/after/drop_routine.snap new file mode 100644 index 00000000..eb590f18 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_routine.snap @@ -0,0 +1,26 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_routine.sql +--- +drop routine public.calculate_total(integer, numeric); + +drop routine if exists + extraordinarily_long_schema_name.extraordinarily_long_calculation_routine( + extraordinarily_long_schema_name.extraordinarily_long_argument_type_name, + double precision + ), + public.another_extraordinarily_long_routine_name( + integer + ) + cascade; + +-- comments in every position +drop /* routine */ routine /* if */ if /* exists */ exists + /* first routine */ public /* dot */.calculate_total /* open */( + /* first argument */ integer /* argument comma */, + /* second argument */ numeric /* close */ + ) /* routine comma */, + /* second routine */ reporting.refresh_cache( + /* argument */ text + ) + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_rule.snap b/crates/squawk_fmt/tests/after/drop_rule.snap new file mode 100644 index 00000000..8ca34019 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_rule.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_rule.sql +--- +drop rule archive_order on public.orders; + +drop rule if exists + extraordinarily_long_rule_name_for_archiving_completed_customer_orders + on extraordinarily_long_schema_name.extraordinarily_long_customer_orders_relation + cascade; + +-- comments in every position +drop /* rule */ rule /* if */ if /* exists */ exists + /* rule name */ archive_order + /* on */ on /* relation schema */ public /* dot */.orders + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_schema.snap b/crates/squawk_fmt/tests/after/drop_schema.snap new file mode 100644 index 00000000..64cd041e --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_schema.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_schema.sql +--- +drop schema analytics; + +drop schema if exists + extraordinarily_long_schema_name_for_customer_reporting_and_business_intelligence, + another_extraordinarily_long_schema_name_for_historical_application_analytics + cascade; + +-- comments in every position +drop /* schema */ schema /* if */ if /* exists */ exists + /* first schema */ analytics /* comma */, + /* second schema */ reporting + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_sequence.snap b/crates/squawk_fmt/tests/after/drop_sequence.snap new file mode 100644 index 00000000..0b547b0e --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_sequence.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_sequence.sql +--- +drop sequence public.order_number_seq; + +drop sequence if exists + extraordinarily_long_schema_name.extraordinarily_long_sequence_name_for_customer_order_numbers, + another_extraordinarily_long_schema_name.another_extraordinarily_long_sequence_name + cascade; + +-- comments in every position +drop /* sequence */ sequence /* if */ if /* exists */ exists + /* first schema */ public /* first dot */.order_number_seq /* comma */, + /* second schema */ accounting /* second dot */.invoice_number_seq + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_server.snap b/crates/squawk_fmt/tests/after/drop_server.snap new file mode 100644 index 00000000..1f911392 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_server.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_server.sql +--- +drop server reporting_server; + +drop server if exists + extraordinarily_long_foreign_server_name_for_customer_reporting_database, + another_extraordinarily_long_foreign_server_name_for_historical_analytics + cascade; + +-- comments in every position +drop /* server */ server /* if */ if /* exists */ exists + /* first server */ reporting_server /* comma */, + /* second server */ archive_server + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_statistics.snap b/crates/squawk_fmt/tests/after/drop_statistics.snap new file mode 100644 index 00000000..32260948 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_statistics.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_statistics.sql +--- +drop statistics public.user_activity_stats; + +drop statistics if exists + extraordinarily_long_schema_name.extraordinarily_long_statistics_name_for_customer_activity, + another_extraordinarily_long_schema_name.another_extraordinarily_long_statistics_name + cascade; + +-- comments in every position +drop /* statistics */ statistics /* if */ if /* exists */ exists + /* first statistics */ public /* dot */.user_stats /* before comma */, + /* second statistics */ reporting.activity_stats + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_subscription.snap b/crates/squawk_fmt/tests/after/drop_subscription.snap index 5335f46e..826364a9 100644 --- a/crates/squawk_fmt/tests/after/drop_subscription.snap +++ b/crates/squawk_fmt/tests/after/drop_subscription.snap @@ -6,4 +6,4 @@ drop subscription if exists renamed_sub restrict; drop /* after drop */ subscription /* before if */ if /* before exists */ exists /* before name */ commented_sub -/* before behavior */ cascade/* before semicolon */; + /* before behavior */ cascade/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/drop_table.snap b/crates/squawk_fmt/tests/after/drop_table.snap new file mode 100644 index 00000000..b5797353 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_table.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_table.sql +--- +drop table public.accounts; + +drop table if exists + extraordinarily_long_schema_name.extraordinarily_long_table_name_for_customer_accounts, + another_extraordinarily_long_schema_name.another_extraordinarily_long_table_name + cascade; + +-- comments in every position +drop /* table */ table /* if */ if /* exists */ exists + /* first table */ public /* dot */.accounts /* before comma */, + /* second table */ archive.old_accounts + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_tablespace.snap b/crates/squawk_fmt/tests/after/drop_tablespace.snap new file mode 100644 index 00000000..4381d4f9 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_tablespace.snap @@ -0,0 +1,12 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_tablespace.sql +--- +drop tablespace fast_storage; + +drop tablespace if exists + extraordinarily_long_tablespace_name_for_historical_customer_reporting_data; + +-- comments in every position +drop /* tablespace */ tablespace /* if */ if /* exists */ exists + /* tablespace */ archive_storage/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_text_search_config.snap b/crates/squawk_fmt/tests/after/drop_text_search_config.snap new file mode 100644 index 00000000..cb121a2e --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_text_search_config.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_text_search_config.sql +--- +drop text search configuration public.english; + +drop text search configuration if exists + extraordinarily_long_schema_name.extraordinarily_long_text_search_configuration_for_customer_documents + cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* configuration */ configuration /* if */ if /* exists */ exists + /* configuration */ public /* dot */.english + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_text_search_dict.snap b/crates/squawk_fmt/tests/after/drop_text_search_dict.snap new file mode 100644 index 00000000..e97eed4e --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_text_search_dict.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_text_search_dict.sql +--- +drop text search dictionary public.english_stem; + +drop text search dictionary if exists + extraordinarily_long_schema_name.extraordinarily_long_text_search_dictionary_for_customer_documents + cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* dictionary */ dictionary /* if */ if /* exists */ exists + /* dictionary */ public /* dot */.english_stem + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_text_search_parser.snap b/crates/squawk_fmt/tests/after/drop_text_search_parser.snap new file mode 100644 index 00000000..a2901f47 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_text_search_parser.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_text_search_parser.sql +--- +drop text search parser public.default_parser; + +drop text search parser if exists + extraordinarily_long_schema_name.extraordinarily_long_text_search_parser_for_customer_documents + cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* parser */ parser /* if */ if /* exists */ exists + /* parser name */ public /* dot */.default_parser + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_text_search_template.snap b/crates/squawk_fmt/tests/after/drop_text_search_template.snap new file mode 100644 index 00000000..683a1b05 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_text_search_template.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_text_search_template.sql +--- +drop text search template public.simple_template; + +drop text search template if exists + extraordinarily_long_schema_name.extraordinarily_long_text_search_template_for_customer_documents + cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* template */ template /* if */ if /* exists */ exists + /* template name */ public /* dot */.simple_template + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_transform.snap b/crates/squawk_fmt/tests/after/drop_transform.snap new file mode 100644 index 00000000..2b86d096 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_transform.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_transform.sql +--- +drop transform for public.customer_document language plpgsql; + +drop transform if exists for + extraordinarily_long_schema_name.extraordinarily_long_customer_document_type_name + language extraordinarily_long_procedural_language_name + cascade; + +-- comments in every position +drop /* transform */ transform /* if */ if /* exists */ exists /* for */ for + /* type */ public /* dot */.customer_document + /* language */ language /* language name */ plpgsql + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_trigger.snap b/crates/squawk_fmt/tests/after/drop_trigger.snap new file mode 100644 index 00000000..7d90a56b --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_trigger.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_trigger.sql +--- +drop trigger audit_trigger on public.accounts; + +drop trigger if exists + extraordinarily_long_audit_trigger_name_for_customer_account_changes + on extraordinarily_long_schema_name.extraordinarily_long_customer_accounts_table + cascade; + +-- comments in every position +drop /* trigger */ trigger /* if */ if /* exists */ exists + /* name */ audit_trigger + /* on */ on /* relation */ public /* dot */.accounts + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_type.snap b/crates/squawk_fmt/tests/after/drop_type.snap new file mode 100644 index 00000000..96b2b2fc --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_type.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_type.sql +--- +drop type public.customer_kind; + +drop type if exists + extraordinarily_long_schema_name.extraordinarily_long_customer_classification_type, + extraordinarily_long_schema_name.another_extraordinarily_long_customer_status_type + cascade; + +-- comments in every position +drop /* type */ type /* if */ if /* exists */ exists + /* first */ public /* dot */.customer_kind /* comma */, + /* second */ reporting.event_kind + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_user.snap b/crates/squawk_fmt/tests/after/drop_user.snap new file mode 100644 index 00000000..dbad4f4c --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_user.snap @@ -0,0 +1,15 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_user.sql +--- +drop user alice; + +drop user if exists + extraordinarily_long_application_reporting_user_name, + extraordinarily_long_customer_support_user_name, + current_user; + +-- comments in every position +drop /* user */ user /* if */ if /* exists */ exists + /* first */ alice /* comma */, + /* second */ current_user/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_user_mapping.snap b/crates/squawk_fmt/tests/after/drop_user_mapping.snap new file mode 100644 index 00000000..29afef08 --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_user_mapping.snap @@ -0,0 +1,14 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_user_mapping.sql +--- +drop user mapping for app_user server app_server; + +drop user mapping if exists for + extraordinarily_long_application_reporting_user_name + server extraordinarily_long_foreign_data_server_name; + +-- comments in every position +drop /* user */ user /* mapping */ mapping /* if */ if /* exists */ exists /* for */ for + /* role */ current_user + /* server */ server /* server name */ remote_server/* end */; diff --git a/crates/squawk_fmt/tests/after/drop_view.snap b/crates/squawk_fmt/tests/after/drop_view.snap new file mode 100644 index 00000000..1c85a6fe --- /dev/null +++ b/crates/squawk_fmt/tests/after/drop_view.snap @@ -0,0 +1,16 @@ +--- +source: crates/squawk_fmt/tests/tests.rs +input_file: crates/squawk_fmt/tests/before/drop_view.sql +--- +drop view public.active_accounts; + +drop view if exists + extraordinarily_long_schema_name.extraordinarily_long_view_name_for_customer_accounts, + another_extraordinarily_long_schema_name.another_extraordinarily_long_archived_accounts_view + cascade; + +-- comments in every position +drop /* view */ view /* if */ if /* exists */ exists + /* first view */ public /* dot */.active_accounts /* before comma */, + /* second view */ reporting.archived_accounts + /* behavior */ restrict/* end */; diff --git a/crates/squawk_fmt/tests/after/import_foreign_schema.snap b/crates/squawk_fmt/tests/after/import_foreign_schema.snap index 777f1f38..db404009 100644 --- a/crates/squawk_fmt/tests/after/import_foreign_schema.snap +++ b/crates/squawk_fmt/tests/after/import_foreign_schema.snap @@ -4,36 +4,24 @@ input_file: crates/squawk_fmt/tests/before/import_foreign_schema.sql --- import foreign schema remote from server foreign_server into local; -import - foreign - schema - remote +import foreign schema remote limit to (records, users) - from - server foreign_server + from server foreign_server into local options ( schema_name 'public' ); -import - foreign - schema - remote +import foreign schema remote except (ignored_records) - from - server foreign_server + from server foreign_server into an_intentionally_long_local_schema_name_that_makes_the_statement_exceed_eighty_characters; /* before import */ -import - /* before foreign */ foreign - /* before schema */ schema - /* before remote */ remote +import /* before foreign */ foreign /* before schema */ schema /* before remote */ remote /* before limit */ limit /* before to */ to /* before open */( /* before table */ records /* before comma */, /* before second */ users /* before close */ ) - /* before from */ from - /* before server */ server /* before server name */ foreign_server + /* before from */ from /* before server */ server /* before server name */ foreign_server /* before into */ into /* before local */ local/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/notify.snap b/crates/squawk_fmt/tests/after/notify.snap index 2d0fe7af..a88c1c4a 100644 --- a/crates/squawk_fmt/tests/after/notify.snap +++ b/crates/squawk_fmt/tests/after/notify.snap @@ -6,11 +6,9 @@ notify events; notify events, 'payload'; -notify - an_intentionally_long_channel_name_that_makes_this_statement_longer_than_eighty_characters, +notify an_intentionally_long_channel_name_that_makes_this_statement_longer_than_eighty_characters, 'an intentionally long payload that also wraps'; /* before notify */ -notify - /* before channel */ events /* before comma */, +notify /* before channel */ events /* before comma */, /* before payload */ 'payload'/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/reassign.snap b/crates/squawk_fmt/tests/after/reassign.snap index a262fb94..ee4b4294 100644 --- a/crates/squawk_fmt/tests/after/reassign.snap +++ b/crates/squawk_fmt/tests/after/reassign.snap @@ -4,20 +4,14 @@ input_file: crates/squawk_fmt/tests/before/reassign.sql --- reassign owned by alice to bob; -reassign - owned - by +reassign owned by alice, group legacy_owner, current_user - to - an_intentionally_long_role_name_that_makes_this_statement_longer_than_eighty_characters; + to an_intentionally_long_role_name_that_makes_this_statement_longer_than_eighty_characters; /* before reassign */ -reassign - /* before owned */ owned - /* before by */ by +reassign /* before owned */ owned /* before by */ by /* before first */ alice /* before comma */, /* before second */ bob - /* before to */ to - /* before new owner */ carol/* before semicolon */; + /* before to */ to /* before new owner */ carol/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/refresh.snap b/crates/squawk_fmt/tests/after/refresh.snap index a5b1435f..6b88ff91 100644 --- a/crates/squawk_fmt/tests/after/refresh.snap +++ b/crates/squawk_fmt/tests/after/refresh.snap @@ -4,17 +4,11 @@ input_file: crates/squawk_fmt/tests/before/refresh.sql --- refresh materialized view public.summary; -refresh - materialized - view - concurrently +refresh materialized view concurrently public.an_intentionally_long_materialized_view_name_that_makes_this_statement_longer_than_eighty_characters with no data; /* before refresh */ -refresh - /* before materialized */ materialized - /* before view */ view - /* before concurrently */ concurrently +refresh /* before materialized */ materialized /* before view */ view /* before concurrently */ concurrently /* before name */ public /* before dot */./* after dot */ summary /* before with */ with /* before no */ no /* before data */ data/* before semicolon */; diff --git a/crates/squawk_fmt/tests/after/select.snap b/crates/squawk_fmt/tests/after/select.snap index 10ff9ae7..292e235a 100644 --- a/crates/squawk_fmt/tests/after/select.snap +++ b/crates/squawk_fmt/tests/after/select.snap @@ -2,7 +2,9 @@ source: crates/squawk_fmt/tests/tests.rs input_file: crates/squawk_fmt/tests/before/select.sql --- -select 1;select 2;select 3; +select 1; +select 2; +select 3; select 'hello'; select now(); diff --git a/crates/squawk_fmt/tests/after/table_constraints.snap b/crates/squawk_fmt/tests/after/table_constraints.snap index 830f4c87..248c1d39 100644 --- a/crates/squawk_fmt/tests/after/table_constraints.snap +++ b/crates/squawk_fmt/tests/after/table_constraints.snap @@ -17,9 +17,9 @@ create table named_constraints ( valid_at tstzrange, constraint pk primary key (id) deferrable initially deferred, constraint name_unique unique ( - first_very_long_unique_column_name, - second_very_long_unique_column_name - ) + first_very_long_unique_column_name, + second_very_long_unique_column_name + ) include ( first_very_long_included_column_name, second_very_long_included_column_name @@ -36,9 +36,9 @@ create table named_constraints ( > a_very_long_check_right_hand_expression_name ), constraint parent_fk foreign key ( - first_very_long_foreign_key_column_name, - second_very_long_foreign_key_column_name - ) + first_very_long_foreign_key_column_name, + second_very_long_foreign_key_column_name + ) references public.parents ( first_very_long_referenced_column_name, second_very_long_referenced_column_name @@ -51,9 +51,9 @@ create table named_constraints ( on update no action not deferrable, constraint no_overlap exclude using gist ( - first_very_long_exclusion_expression with =, - second_very_long_exclusion_expression with && - ) + first_very_long_exclusion_expression with =, + second_very_long_exclusion_expression with && + ) include ( first_very_long_excluded_column_name, second_very_long_excluded_column_name @@ -87,17 +87,17 @@ create table commented_constraints ( id bigint, parent_id bigint, valid_at tstzrange, - /* before constraint */ constraint /* before constraint name */ named_pk /* before primary */ primary /* before key */ key/* before column opening paren */ ( - /* before column */ id /* before column closing paren */ - ) + /* before constraint */ constraint /* before constraint name */ named_pk /* before primary */ primary /* before key */ key /* before column opening paren */ ( + /* before column */ id /* before column closing paren */ + ) /* before deferrable */ deferrable, constraint named_check /* before check */ check /* before check opening paren */ ( /* before check expression */ id > 0 /* before check closing paren */ ) /* before not */ not /* before valid */ valid, constraint named_fk /* before foreign */ foreign /* before key */ key /* before from opening paren */ ( - /* before from column */ parent_id /* before from closing paren */ - ) + /* before from column */ parent_id /* before from closing paren */ + ) /* before references */ references /* before table */ public /* before dot */./* before table name */ parents /* before to opening paren */ ( /* before to column */ id /* before to closing paren */ ) @@ -108,11 +108,11 @@ create table commented_constraints ( /* before on update */ on /* before update */ update /* before cascade */ cascade /* before enforced */ enforced, constraint named_exclude /* before exclude */ exclude /* before using */ using /* before method */ gist /* before exclusion opening paren */ ( - /* before exclusion expression */ id - /* before exclusion with */ with /* before exclusion op */ = /* before exclusion comma */, - /* before second exclusion */ valid_at - with /* before operator */ operator /* before operator opening paren */(/* before operator name */ public /* before operator dot */./* before operator op */ && /* before operator closing paren */) /* before exclusion closing paren */ - ) + /* before exclusion expression */ id + /* before exclusion with */ with /* before exclusion op */ = /* before exclusion comma */, + /* before second exclusion */ valid_at + with /* before operator */ operator /* before operator opening paren */(/* before operator name */ public /* before operator dot */./* before operator op */ && /* before operator closing paren */) /* before exclusion closing paren */ + ) /* before include */ include /* before include opening paren */ ( id /* before include closing paren */ ) @@ -120,7 +120,7 @@ create table commented_constraints ( /* before param */ fillfactor /* before equals */ = /* before value */ 80 /* before params closing paren */ ) /* before tablespace using */ using /* before index */ index /* before tablespace */ tablespace /* before tablespace name */ fast - /* before where */ where /* before where opening paren */( + /* before where */ where /* before where opening paren */ ( /* before where expression */ id > 0 /* before where closing paren */ ) /* before initially */ initially /* before immediate */ immediate diff --git a/crates/squawk_fmt/tests/before/alter_aggregate.sql b/crates/squawk_fmt/tests/before/alter_aggregate.sql new file mode 100644 index 00000000..f42b9c24 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_aggregate.sql @@ -0,0 +1,9 @@ +alter aggregate public.total_sales(numeric) rename to total_revenue; + +alter aggregate public.total_sales(numeric) owner to reporting_role; + +alter aggregate public.total_sales(numeric) set schema reporting; + +alter aggregate extraordinarily_long_schema_name.extraordinarily_long_aggregate_name(double precision, numeric, bigint) rename to another_extraordinarily_long_aggregate_name; + +alter /* aggregate keyword */ aggregate /* aggregate signature */ public.commented_total(/* first parameter */ numeric, /* second parameter */ bigint) /* action */ rename /* to keyword */ to /* new name */ commented_revenue /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_collation.sql b/crates/squawk_fmt/tests/before/alter_collation.sql new file mode 100644 index 00000000..330d9c18 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_collation.sql @@ -0,0 +1,13 @@ +alter collation public.en_us rename to en_us_utf8; + +alter collation public.en_us owner to localization_admin; + +alter collation public.en_us refresh version; + +alter collation public.en_us set schema localization; + +alter collation extraordinarily_long_schema_name.extraordinarily_long_collation_name rename to another_extraordinarily_long_collation_name; + +alter /* collation keyword */ collation /* collation name */ public.commented_en_us /* action */ refresh /* version keyword */ version /* semicolon */; + +alter collation /* name */ public.comment_positions /* action */ set /* schema keyword */ schema /* schema name */ commented_localization; diff --git a/crates/squawk_fmt/tests/before/alter_conversion.sql b/crates/squawk_fmt/tests/before/alter_conversion.sql new file mode 100644 index 00000000..1254d22a --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_conversion.sql @@ -0,0 +1,11 @@ +alter conversion public.latin1_to_utf8 rename to latin_to_utf8; + +alter conversion public.latin1_to_utf8 owner to encoding_admin; + +alter conversion public.latin1_to_utf8 set schema encodings; + +alter conversion extraordinarily_long_schema_name.extraordinarily_long_conversion_name rename to another_extraordinarily_long_conversion_name; + +alter /* conversion keyword */ conversion /* conversion name */ public.commented_conversion /* action */ rename /* to keyword */ to /* new name */ renamed_conversion /* semicolon */; + +alter conversion /* conversion name */ public.schema_comments /* action */ set /* schema keyword */ schema /* schema name */ archived_encodings; diff --git a/crates/squawk_fmt/tests/before/alter_database.sql b/crates/squawk_fmt/tests/before/alter_database.sql new file mode 100644 index 00000000..d73cadcf --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_database.sql @@ -0,0 +1,23 @@ +alter database app rename to application; + +alter database app owner to app_admin; + +alter database app refresh collation version; + +alter database app reset all; + +alter database app reset statement_timeout; + +alter database app set statement_timeout to '10s'; + +alter database app set tablespace fast_storage; + +alter database app with connection limit = 100 encoding = 'UTF8' lc_collate = default owner = app_admin tablespace = fast_storage template = template0; + +alter database extraordinarily_long_database_name with connection limit = 1000 encoding = 'UTF8' owner = extraordinarily_long_database_owner tablespace = extraordinarily_long_tablespace_name; + +alter /* database keyword */ database /* database name */ commented_app /* action */ with /* option */ connection /* limit keyword */ limit /* equals */ = /* value */ 25 /* next option */ encoding /* equals */ = /* value */ 'UTF8' /* owner option */ owner /* equals */ = /* owner value */ app_admin /* tablespace option */ tablespace /* equals */ = /* tablespace value */ fast_storage /* template option */ template /* equals */ = /* template value */ template0 /* semicolon */; + +alter database /* name */ commented_config /* action */ set /* parameter */ statement_timeout /* assignment */ to /* value */ '30s'; + +alter database /* name */ commented_refresh /* action */ refresh /* collation keyword */ collation /* version keyword */ version /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_default_privileges.sql b/crates/squawk_fmt/tests/before/alter_default_privileges.sql new file mode 100644 index 00000000..69ea2853 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_default_privileges.sql @@ -0,0 +1,17 @@ +alter default privileges grant select on tables to reporting; + +alter default privileges for role app_owner, migrations in schema public, audit grant all privileges on sequences to app_user, reporting with grant option; + +alter default privileges for user app_owner revoke grant option for insert, update on tables from app_user cascade; + +alter default privileges grant execute on functions to public; + +alter default privileges grant execute on routines to public; + +alter default privileges grant usage on schemas to public; + +alter default privileges grant usage on types to public; + +alter default privileges grant select on large objects to public; + +alter /* default keyword */ default /* privileges keyword */ privileges /* for clause */ for /* role keyword */ role /* first role */ exceedingly_long_application_owner, /* second role */ exceedingly_long_migration_owner /* schemas clause */ in /* schema keyword */ schema /* first schema */ exceedingly_long_application_schema, /* second schema */ exceedingly_long_audit_schema /* action */ grant /* privilege */ select, /* another privilege */ update /* on keyword */ on /* target */ tables /* to keyword */ to /* grantee */ exceedingly_long_reporting_role /* with clause */ with /* grant keyword */ grant /* option keyword */ option /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_domain.sql b/crates/squawk_fmt/tests/before/alter_domain.sql new file mode 100644 index 00000000..b45166d5 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_domain.sql @@ -0,0 +1,23 @@ +alter domain public.email_address set default 'unknown@example.com'; + +alter domain public.email_address drop default; + +alter domain public.email_address set not null; + +alter domain public.email_address drop not null; + +alter domain public.email_address add constraint email_format check (value ~ '@'); + +alter domain public.email_address drop constraint if exists old_email_format cascade; + +alter domain public.email_address rename constraint email_format to valid_email_format; + +alter domain public.email_address validate constraint valid_email_format; + +alter domain public.email_address owner to app_owner; + +alter domain public.email_address rename to contact_email_address; + +alter domain public.email_address set schema application_types; + +alter /* domain */ domain /* name */ exceedingly_long_schema_name.exceedingly_long_domain_name /* action */ add /* constraint keyword */ constraint /* constraint name */ exceedingly_long_constraint_name /* check */ check /* expression */ (value is not null and length(value) > 10) /* not valid */ not /* valid */ valid /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_event_trigger.sql b/crates/squawk_fmt/tests/before/alter_event_trigger.sql new file mode 100644 index 00000000..5e18253f --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_event_trigger.sql @@ -0,0 +1,13 @@ +alter event trigger ddl_audit enable; + +alter event trigger ddl_audit disable; + +alter event trigger ddl_audit enable always; + +alter event trigger ddl_audit enable replica; + +alter event trigger ddl_audit owner to audit_owner; + +alter event trigger ddl_audit rename to renamed_ddl_audit; + +alter /* event */ event /* trigger */ trigger /* name */ an_exceedingly_long_event_trigger_name_used_for_auditing /* action */ enable /* always */ always /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_extension.sql b/crates/squawk_fmt/tests/before/alter_extension.sql new file mode 100644 index 00000000..e2d8133b --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_extension.sql @@ -0,0 +1,79 @@ +alter extension hstore update; + +alter extension hstore update to '1.8'; + +alter extension hstore set schema extensions; + +alter extension hstore add access method custom_heap; + +alter extension hstore add aggregate public.total(integer); + +alter extension hstore add cast (integer as text); + +alter extension hstore add collation public.english; + +alter extension hstore add conversion public.latin_conversion; + +alter extension hstore add database app_db; + +alter extension hstore add domain public.email_address; + +alter extension hstore add event trigger ddl_audit; + +alter extension hstore add extension dependent_extension; + +alter extension hstore add foreign data wrapper app_fdw; + +alter extension hstore add foreign table public.remote_accounts; + +alter extension hstore add function public.calculate(integer, text); + +alter extension hstore add index public.accounts_email_idx; + +alter extension hstore add procedural language plpgsql; + +alter extension hstore add materialized view public.account_totals; + +alter extension hstore add operator public.=== (integer, integer); + +alter extension hstore add operator class public.custom_ops using btree; + +alter extension hstore add operator family public.custom_family using btree; + +alter extension hstore add procedure public.refresh_accounts(integer); + +alter extension hstore add publication account_changes; + +alter extension hstore add role app_owner; + +alter extension hstore add routine public.normalize(text); + +alter extension hstore add schema application; + +alter extension hstore add sequence public.account_ids; + +alter extension hstore add server remote_server; + +alter extension hstore add statistics public.account_stats; + +alter extension hstore add subscription account_subscription; + +alter extension hstore drop table public.old_accounts; + +alter extension hstore add tablespace fast_storage; + +alter extension hstore add text search configuration public.english_config; + +alter extension hstore add text search dictionary public.english_dict; + +alter extension hstore add text search parser public.english_parser; + +alter extension hstore add text search template public.english_template; + +alter extension hstore add transform for jsonb language plpgsql; + +alter extension hstore add type public.account_state; + +alter extension hstore add view public.active_accounts; + +alter /* extension */ extension /* name */ an_exceedingly_long_extension_name_for_application_features /* action */ add /* object kind */ operator /* operator name */ application_schema.=== /* open paren */ (/* left type */ application_schema.exceedingly_long_left_operand_type, /* right type */ application_schema.exceedingly_long_right_operand_type /* close paren */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_foreign_data_wrapper.sql b/crates/squawk_fmt/tests/before/alter_foreign_data_wrapper.sql new file mode 100644 index 00000000..2ab671c9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_foreign_data_wrapper.sql @@ -0,0 +1,9 @@ +ALTER /* foreign */ FOREIGN /* data */ DATA /* wrapper */ WRAPPER /* name */ fdw /* options */ OPTIONS /* left */ (/* add */ ADD /* key */ host /* value */ 'localhost', /* set */ SET /* key */ port /* value */ '5432', /* drop */ DROP /* key */ obsolete /* right */); + +ALTER FOREIGN DATA WRAPPER very_long_foreign_data_wrapper_name HANDLER public.very_long_handler_function_name; + +ALTER FOREIGN DATA WRAPPER fdw NO VALIDATOR; + +ALTER FOREIGN DATA WRAPPER fdw RENAME /* to */ TO /* name */ renamed_fdw; + +ALTER FOREIGN DATA WRAPPER fdw OWNER /* to */ TO /* role */ current_user; diff --git a/crates/squawk_fmt/tests/before/alter_foreign_table.sql b/crates/squawk_fmt/tests/before/alter_foreign_table.sql new file mode 100644 index 00000000..fe74e12c --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_foreign_table.sql @@ -0,0 +1,7 @@ +ALTER /* foreign */ FOREIGN /* table */ TABLE /* if */ IF /* not */ EXISTS /* table name */ public.very_long_foreign_table_name ADD /* column */ COLUMN /* if */ IF /* not */ NOT /* exists */ EXISTS /* column name */ column_with_a_very_long_descriptive_name /* type */ varchar(255) /* options */ OPTIONS (/* key */ remote_name /* value */ 'remote_column'), /* drop */ DROP /* column */ COLUMN /* if */ IF /* exists */ EXISTS /* name */ obsolete_column /* behavior */ CASCADE; + +ALTER FOREIGN TABLE public.items ALTER COLUMN quantity SET DEFAULT 100, ALTER COLUMN description TYPE text USING description::text; + +ALTER FOREIGN TABLE public.items ADD CONSTRAINT positive_quantity CHECK (quantity > 0), RENAME COLUMN old_name TO new_name; + +ALTER FOREIGN TABLE IF EXISTS public.items OWNER TO role_with_a_very_long_descriptive_name; diff --git a/crates/squawk_fmt/tests/before/alter_function.sql b/crates/squawk_fmt/tests/before/alter_function.sql new file mode 100644 index 00000000..5edcd499 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_function.sql @@ -0,0 +1,13 @@ +ALTER /* function */ FUNCTION /* signature */ public.calculate_a_very_long_and_descriptive_result(/* type */ integer, /* type */ text) /* action */ SECURITY /* definer */ DEFINER /* restrict */ RESTRICT; + +ALTER FUNCTION public.f(integer) /* action */ SET /* parameter */ work_mem /* to */ TO /* value */ '64MB'; + +ALTER FUNCTION public.f(integer) DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ extension_name; + +ALTER FUNCTION public.f(integer) NO /* depends */ DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ extension_name; + +ALTER FUNCTION public.f(integer) RENAME /* to */ TO /* name */ renamed_function; + +ALTER FUNCTION public.f(integer) OWNER /* to */ TO /* role */ current_role; + +ALTER FUNCTION public.f(integer) SET /* schema */ SCHEMA /* name */ archive; diff --git a/crates/squawk_fmt/tests/before/alter_group.sql b/crates/squawk_fmt/tests/before/alter_group.sql new file mode 100644 index 00000000..fb50e0b5 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_group.sql @@ -0,0 +1,5 @@ +ALTER /* group */ GROUP /* group name */ administrators /* action */ ADD /* user */ USER /* first */ alice, /* second */ bob, /* long */ user_with_an_exceptionally_long_name; + +ALTER GROUP administrators DROP /* user */ USER /* first */ alice, /* second */ bob; + +ALTER GROUP administrators RENAME /* to */ TO /* name */ application_administrators_with_a_very_long_name; diff --git a/crates/squawk_fmt/tests/before/alter_index.sql b/crates/squawk_fmt/tests/before/alter_index.sql new file mode 100644 index 00000000..ff44c73e --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_index.sql @@ -0,0 +1,19 @@ +ALTER /* index */ INDEX /* all */ ALL /* in */ IN /* tablespace */ TABLESPACE /* old */ old_tablespace_with_a_very_long_descriptive_name /* owned */ OWNED /* by */ BY /* role one */ first_role_with_a_long_name, /* role two */ second_role_with_a_long_name /* set */ SET /* tablespace */ TABLESPACE /* new */ new_tablespace_with_a_very_long_descriptive_name /* nowait */ NOWAIT /* end */; + +ALTER INDEX /* if */ IF /* exists */ EXISTS /* name */ public.index_with_a_very_long_descriptive_name /* alter */ ALTER /* column */ COLUMN /* number */ 3 /* set */ SET /* statistics */ STATISTICS /* value */ 1000; + +ALTER INDEX idx ATTACH /* partition */ PARTITION /* child */ public.child_idx; + +ALTER INDEX idx /* depends */ DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ bloom; + +ALTER INDEX idx /* no */ NO /* depends */ DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ bloom; + +ALTER INDEX idx /* rename */ RENAME /* to */ TO /* name */ renamed_idx; + +ALTER INDEX idx /* reset */ RESET /* list */ (/* first */ fillfactor, /* second */ deduplicate_items); + +ALTER INDEX idx /* set */ SET /* list */ (/* key */ fillfactor /* equals */ = /* value */ 70, deduplicate_items = off); + +ALTER INDEX idx ALTER COLUMN indexed_expression SET (/* key */ n_distinct = /* value */ 100); + +ALTER INDEX idx /* set */ SET /* tablespace */ TABLESPACE /* name */ fast_storage; diff --git a/crates/squawk_fmt/tests/before/alter_language.sql b/crates/squawk_fmt/tests/before/alter_language.sql new file mode 100644 index 00000000..eb0b6306 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_language.sql @@ -0,0 +1,7 @@ +ALTER /* procedural */ PROCEDURAL /* language */ LANGUAGE /* name */ plpgsql /* rename */ RENAME /* to */ TO /* new name */ plpgsql_new_name_with_a_very_long_descriptive_suffix /* end */; + +ALTER /* language */ LANGUAGE /* name */ plpgsql /* owner */ OWNER /* to */ TO /* role */ language_owner_with_a_very_long_descriptive_name; + +ALTER LANGUAGE old_language RENAME TO new_language; + +ALTER LANGUAGE plpgsql OWNER TO CURRENT_USER; diff --git a/crates/squawk_fmt/tests/before/alter_large_object.sql b/crates/squawk_fmt/tests/before/alter_large_object.sql new file mode 100644 index 00000000..cdb416bf --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_large_object.sql @@ -0,0 +1,3 @@ +ALTER /* large */ LARGE /* object */ OBJECT /* id */ 1234567890 /* owner */ OWNER /* to */ TO /* role */ large_object_owner_with_a_very_long_descriptive_name /* end */; + +ALTER LARGE OBJECT 987654321 OWNER TO CURRENT_ROLE; diff --git a/crates/squawk_fmt/tests/before/alter_materialized_view.sql b/crates/squawk_fmt/tests/before/alter_materialized_view.sql new file mode 100644 index 00000000..a169ea45 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_materialized_view.sql @@ -0,0 +1,15 @@ +ALTER /* materialized */ MATERIALIZED /* view */ VIEW /* all */ ALL /* in */ IN /* tablespace */ TABLESPACE /* old */ old_materialized_view_tablespace /* owned */ OWNED /* by */ BY /* role */ analytics_owner_with_a_very_long_name /* set */ SET /* tablespace */ TABLESPACE /* new */ new_materialized_view_tablespace /* nowait */ NOWAIT /* end */; + +ALTER MATERIALIZED VIEW /* if */ IF /* exists */ EXISTS /* view */ reporting.materialized_view_with_a_very_long_descriptive_name /* alter */ ALTER /* column */ COLUMN /* name */ measured_value /* set */ SET /* statistics */ STATISTICS /* value */ 1000, /* owner */ OWNER /* to */ TO /* role */ analytics_owner_with_a_very_long_name; + +ALTER MATERIALIZED VIEW reporting.summary /* rename */ RENAME /* column */ COLUMN /* old */ old_column /* to */ TO /* new */ new_column; + +ALTER MATERIALIZED VIEW reporting.summary /* rename */ RENAME /* to */ TO /* view */ renamed_summary; + +ALTER MATERIALIZED VIEW reporting.summary /* set */ SET /* schema */ SCHEMA /* name */ archive; + +ALTER MATERIALIZED VIEW reporting.summary /* depends */ DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ tablefunc; + +ALTER MATERIALIZED VIEW reporting.summary /* no */ NO /* depends */ DEPENDS /* on */ ON /* extension */ EXTENSION /* name */ tablefunc; + +ALTER MATERIALIZED VIEW reporting.summary SET (fillfactor = 80); diff --git a/crates/squawk_fmt/tests/before/alter_operator.sql b/crates/squawk_fmt/tests/before/alter_operator.sql new file mode 100644 index 00000000..20dcdec1 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_operator.sql @@ -0,0 +1,5 @@ +ALTER /* operator */ OPERATOR /* signature */ public.+ /* left paren */ (/* left type */ integer /* comma */, /* right type */ integer /* right paren */) /* owner */ OWNER /* to */ TO /* role */ application_owner /* end */; + +ALTER OPERATOR public.## (NONE, integer) SET (RESTRICT = schema_a.restrict_function_with_a_very_long_name, JOIN = schema_a.join_function_with_a_very_long_name, HASHES, MERGES); + +ALTER OPERATOR public.+ (integer, integer) /* set */ SET /* schema */ SCHEMA /* name */ archive; diff --git a/crates/squawk_fmt/tests/before/alter_operator_class.sql b/crates/squawk_fmt/tests/before/alter_operator_class.sql new file mode 100644 index 00000000..4f3fd871 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_operator_class.sql @@ -0,0 +1,5 @@ +ALTER /* operator */ OPERATOR /* class */ CLASS /* name */ public.integer_operations /* using */ USING /* method */ btree /* rename */ RENAME /* to */ TO /* new name */ integer_operations_for_archived_measurements /* end */; + +ALTER OPERATOR CLASS public.integer_operations USING btree OWNER TO application_owner_with_a_very_long_descriptive_name; + +ALTER OPERATOR CLASS public.integer_operations USING btree /* set */ SET /* schema */ SCHEMA /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/before/alter_operator_family.sql b/crates/squawk_fmt/tests/before/alter_operator_family.sql new file mode 100644 index 00000000..d435d49b --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_operator_family.sql @@ -0,0 +1,9 @@ +ALTER /* operator */ OPERATOR /* family */ FAMILY /* name */ public.integer_family /* using */ USING /* method */ btree /* add */ ADD /* operator option */ OPERATOR /* strategy */ 1 /* op */ < /* left paren */ (/* lhs */ integer /* comma */, /* rhs */ integer /* right paren */) /* for */ FOR /* order */ ORDER /* by */ BY /* family */ public.sort_family /* option comma */, /* function option */ FUNCTION /* support */ 1 /* argument types */ (integer, integer) /* function */ public.compare_integer_values(integer, integer) /* option comma */, /* storage */ STORAGE /* type */ bigint /* end */; + +ALTER OPERATOR FAMILY public.integer_family USING btree /* drop */ DROP /* operator */ OPERATOR /* strategy */ 1 /* params */ (integer, integer) /* comma */, /* function */ FUNCTION /* support */ 1 /* params */ (integer, integer); + +ALTER OPERATOR FAMILY public.integer_family USING btree RENAME TO integer_family_for_archived_measurements; + +ALTER OPERATOR FAMILY public.integer_family USING btree OWNER TO application_owner; + +ALTER OPERATOR FAMILY public.integer_family USING btree SET SCHEMA archive; diff --git a/crates/squawk_fmt/tests/before/alter_policy.sql b/crates/squawk_fmt/tests/before/alter_policy.sql new file mode 100644 index 00000000..f5d76d28 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_policy.sql @@ -0,0 +1,5 @@ +ALTER /* policy */ POLICY /* name */ account_access_policy /* on */ ON /* table */ public.accounts /* to */ TO /* first role */ account_administrator, /* second role */ account_auditor /* using */ USING /* left paren */ (/* expression */ owner_identifier = current_user /* right paren */) /* with */ WITH /* check */ CHECK /* left paren */ (/* expression */ account_is_active AND account_identifier > 1000000 /* right paren */) /* end */; + +ALTER POLICY account_access_policy ON public.accounts USING (account_identifier = a_very_long_session_account_identifier_function(current_user, current_role)); + +ALTER POLICY account_access_policy ON public.accounts /* rename */ RENAME /* to */ TO /* new name */ archived_account_access_policy; diff --git a/crates/squawk_fmt/tests/before/alter_procedure.sql b/crates/squawk_fmt/tests/before/alter_procedure.sql new file mode 100644 index 00000000..12d82d9e --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_procedure.sql @@ -0,0 +1,5 @@ +alter /* procedure */ procedure /* signature */ public.recalculate_account_totals(/* arg */ integer, text) /* action */ rename /* to */ to /* target */ recalculate_totals /* end */; + +alter procedure process_customer_transactions_with_an_exceptionally_long_name(integer, text) security definer set work_mem = '256MB' reset all restrict; + +alter procedure p no depends on extension /* extension */ auditing_tools; diff --git a/crates/squawk_fmt/tests/before/alter_property_graph.sql b/crates/squawk_fmt/tests/before/alter_property_graph.sql new file mode 100644 index 00000000..c8cd8c17 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_property_graph.sql @@ -0,0 +1,11 @@ +alter /* property */ property /* graph */ graph /* if */ if /* exists */ exists /* name */ public.social_graph /* action */ rename /* to */ to /* target */ archived_graph /* end */; + +alter property graph graph_with_an_exceptionally_long_descriptive_name add vertex tables (customer_accounts key (customer_identifier) no properties, merchant_accounts key (merchant_identifier) properties all columns); + +alter property graph social_graph alter /* kind */ vertex /* table */ table /* element */ people alter /* label */ label /* name */ person add /* properties */ properties (full_name, birth_date as date_of_birth); + +alter property graph social_graph alter relationship table follows alter label connection drop properties (created_at, source_system); + +alter property graph social_graph drop vertex tables (people, organizations) cascade; + +alter property graph social_graph owner /* to */ to /* role */ graph_administrator; diff --git a/crates/squawk_fmt/tests/before/alter_role.sql b/crates/squawk_fmt/tests/before/alter_role.sql new file mode 100644 index 00000000..4c4ba8e9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_role.sql @@ -0,0 +1,7 @@ +alter /* role */ role /* name */ reporting_user /* in */ in /* database */ database /* db */ analytics /* action */ set /* parameter */ work_mem /* assignment */ to /* value */ '128MB' /* end */; + +alter role exceptionally_long_application_service_account_name with superuser createdb createrole login replication bypassrls connection limit 100 encrypted password 'a-long-secret-value' valid until '2042-02-22'; + +alter role current_user rename /* to */ to /* target */ renamed_user; + +alter role all reset /* all */ all; diff --git a/crates/squawk_fmt/tests/before/alter_routine.sql b/crates/squawk_fmt/tests/before/alter_routine.sql new file mode 100644 index 00000000..6381cb80 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_routine.sql @@ -0,0 +1,5 @@ +alter /* routine */ routine /* signature */ public.calculate_customer_balance(/* arg */ integer) /* action */ owner /* to */ to /* owner */ current_user /* end */; + +alter routine calculate_customer_balance_for_all_historical_transactions(integer, text) immutable parallel safe cost 1000 rows 10000 restrict; + +alter routine r /* no */ no depends /* on */ on extension /* name */ helper_extension; diff --git a/crates/squawk_fmt/tests/before/alter_rule.sql b/crates/squawk_fmt/tests/before/alter_rule.sql new file mode 100644 index 00000000..d9adf4f3 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_rule.sql @@ -0,0 +1,5 @@ +alter rule old_rule on public.orders rename to current_orders; + +alter rule rule_with_a_very_long_descriptive_name on reporting.relation_with_a_very_long_descriptive_name rename to renamed_rule_with_a_very_long_descriptive_name; + +alter /* before rule */ rule /* before old name */ commented_rule /* before on */ on /* before relation */ public.commented_orders /* before rename */ rename /* before to */ to /* before new name */ renamed_commented_rule /* before semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_schema.sql b/crates/squawk_fmt/tests/before/alter_schema.sql new file mode 100644 index 00000000..f3b0174c --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_schema.sql @@ -0,0 +1,9 @@ +alter schema accounting rename to finance; + +alter schema reporting owner to reporting_admin; + +alter schema schema_with_a_very_long_descriptive_name rename to schema_with_an_even_longer_descriptive_replacement_name; + +alter /* before schema */ schema /* before name */ commented_schema /* before rename */ rename /* before to */ to /* before new name */ renamed_commented_schema /* before semicolon */; + +alter schema another_commented_schema /* before owner */ owner /* before to */ to /* before role */ current_user; diff --git a/crates/squawk_fmt/tests/before/alter_sequence.sql b/crates/squawk_fmt/tests/before/alter_sequence.sql new file mode 100644 index 00000000..7d054b06 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_sequence.sql @@ -0,0 +1,19 @@ +alter sequence public.invoice_number increment by 10 minvalue 1 maxvalue 999999999 start with 100 cache 50 cycle owned by public.invoices.invoice_number; + +alter sequence if exists public.invoice_number as bigint no minvalue no maxvalue restart with 500 no cycle owned by none; + +alter sequence public.invoice_number set logged; + +alter sequence public.invoice_number set unlogged; + +alter sequence public.invoice_number owner to sequence_admin; + +alter sequence public.invoice_number rename to archived_invoice_number; + +alter sequence public.invoice_number set schema archive; + +alter /* before sequence */ sequence /* before if */ if /* before exists */ exists /* before name */ public.commented_sequence /* before increment */ increment /* before by */ by /* before value */ 100 /* before minvalue */ minvalue /* before minimum */ 1 /* before maxvalue */ maxvalue /* before maximum */ 999999 /* before start */ start /* before with */ with /* before start value */ 10 /* before cache */ cache /* before cache value */ 20 /* before cycle */ no /* before cycle keyword */ cycle /* before owned */ owned /* before owned by */ by /* before owner target */ public.orders.sequence_id /* before semicolon */; + +alter sequence public.other_sequence /* before set */ set /* before logged */ logged; + +alter sequence public.third_sequence /* before owner */ owner /* before to */ to /* before role */ current_role; diff --git a/crates/squawk_fmt/tests/before/alter_server.sql b/crates/squawk_fmt/tests/before/alter_server.sql new file mode 100644 index 00000000..cf9ca083 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_server.sql @@ -0,0 +1,11 @@ +alter server analytics version 'postgresql-17' options (add host 'analytics.internal.example', set port '5432', drop obsolete_setting); + +alter server analytics owner to foreign_data_admin; + +alter server analytics rename to analytics_primary; + +alter server foreign_server_with_a_very_long_descriptive_name version 'postgresql-17-with-a-very-long-version-description' options (add host 'database-with-a-long-name.internal.example', set application_name 'squawk formatter integration test', drop obsolete_connection_setting); + +alter /* before server */ server /* before name */ commented_server /* before version */ version /* before version value */ '17.0' /* before options */ options /* before left paren */ (/* before add */ add /* before option name */ host /* before option value */ 'db.internal', /* after comma */ set /* before set name */ port /* before set value */ '5432', /* second comma */ drop /* before drop name */ old_port /* before right paren */) /* before semicolon */; + +alter server another_commented_server /* before owner */ owner /* before to */ to /* before role */ session_user; diff --git a/crates/squawk_fmt/tests/before/alter_statistics.sql b/crates/squawk_fmt/tests/before/alter_statistics.sql new file mode 100644 index 00000000..3a59ebdd --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_statistics.sql @@ -0,0 +1,13 @@ +ALTER STATISTICS reporting.user_activity_statistics OWNER TO analytics_administrator; + +ALTER STATISTICS reporting.user_activity_statistics RENAME TO user_activity_statistics_for_archived_reporting; + +ALTER STATISTICS reporting.user_activity_statistics SET SCHEMA reporting_archive; + +ALTER STATISTICS reporting.user_activity_statistics SET STATISTICS 1000; + +ALTER STATISTICS reporting.user_activity_statistics SET STATISTICS DEFAULT; + +ALTER /* statistics */ STATISTICS /* name */ reporting.commented_statistics /* action */ RENAME /* to */ TO /* new name */ reporting.renamed_commented_statistics /* semicolon */; + +ALTER STATISTICS reporting.statistics_with_a_very_long_and_descriptive_name SET STATISTICS /* expression */ 10000; diff --git a/crates/squawk_fmt/tests/before/alter_system.sql b/crates/squawk_fmt/tests/before/alter_system.sql new file mode 100644 index 00000000..38e5755d --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_system.sql @@ -0,0 +1,15 @@ +ALTER SYSTEM SET work_mem = '64MB'; + +ALTER SYSTEM SET archive_command TO 'cp %p /very/long/archive/destination/with/a/descriptive/directory/%f'; + +ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements', /* second value */ 'auto_explain'; + +ALTER SYSTEM SET effective_cache_size FROM CURRENT; + +ALTER SYSTEM RESET work_mem; + +ALTER SYSTEM RESET ALL; + +ALTER /* system */ SYSTEM /* action */ SET /* parameter */ autovacuum_vacuum_cost_limit /* equals */ = /* value */ 4000 /* semicolon */; + +ALTER /* system */ SYSTEM /* reset */ RESET /* parameter */ autovacuum_analyze_scale_factor /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_table.sql b/crates/squawk_fmt/tests/before/alter_table.sql new file mode 100644 index 00000000..33d7a1a1 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_table.sql @@ -0,0 +1,19 @@ +ALTER TABLE customers ADD COLUMN email_address varchar(320), ALTER COLUMN customer_description SET DEFAULT 'No description has been provided for this customer', DROP COLUMN IF EXISTS obsolete_customer_code CASCADE; + +ALTER TABLE IF EXISTS ONLY (reporting.customer_activity) RENAME COLUMN old_activity_description TO new_activity_description; + +ALTER TABLE reporting.customer_activity ADD CONSTRAINT positive_activity_count CHECK (activity_count > 0), ALTER CONSTRAINT positive_activity_count DEFERRABLE INITIALLY DEFERRED; + +ALTER TABLE reporting.customer_activity ATTACH PARTITION reporting.customer_activity_2025 FOR VALUES FROM ('2025-01-01') TO ('2026-01-01'); + +ALTER TABLE reporting.customer_activity DETACH PARTITION reporting.customer_activity_2024 CONCURRENTLY; + +ALTER TABLE reporting.customer_activity ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY, DISABLE TRIGGER ALL; + +ALTER TABLE reporting.customer_activity SET (fillfactor = 80), RESET (fillfactor), SET TABLESPACE archive_tablespace; + +ALTER TABLE ALL IN TABLESPACE old_reporting_tablespace OWNED BY analytics_owner, reporting_owner SET TABLESPACE new_reporting_tablespace NOWAIT; + +ALTER /* table */ TABLE /* if */ IF /* exists */ EXISTS /* only */ ONLY /* left paren */ (/* relation */ reporting.commented_customer_activity /* right paren */) /* action */ ALTER /* column */ COLUMN /* column name */ activity_description /* set */ SET /* default */ DEFAULT /* expression */ 'A very long default activity description used to verify formatter wrapping behavior', /* comma */ RENAME /* column */ COLUMN /* old name */ old_activity_code /* to */ TO /* new name */ current_activity_code /* semicolon */; + +ALTER /* table */ TABLE /* all */ ALL /* in */ IN /* tablespace keyword */ TABLESPACE /* old tablespace */ old_tablespace /* owned */ OWNED /* by */ BY /* first role */ role_one, /* second role */ role_two /* set */ SET /* tablespace */ TABLESPACE /* new tablespace */ new_tablespace /* nowait */ NOWAIT /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_tablespace.sql b/crates/squawk_fmt/tests/before/alter_tablespace.sql new file mode 100644 index 00000000..e9f244f6 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_tablespace.sql @@ -0,0 +1,15 @@ +ALTER TABLESPACE application_data OWNER TO storage_administrator; + +ALTER TABLESPACE application_data RENAME TO application_data_for_archived_customer_records; + +ALTER TABLESPACE application_data SET (seq_page_cost = 1.0, random_page_cost = 1.1, effective_io_concurrency = 200); + +ALTER TABLESPACE application_data RESET (seq_page_cost, random_page_cost, effective_io_concurrency); + +ALTER /* tablespace */ TABLESPACE /* name */ commented_application_data /* action */ OWNER /* to */ TO /* owner */ storage_administrator_with_a_very_long_name /* semicolon */; + +ALTER /* tablespace */ TABLESPACE /* name */ commented_options /* action */ SET /* left paren */ (/* option */ seq_page_cost /* equals */ = /* value */ 1.0, /* second option */ random_page_cost /* second equals */ = /* second value */ 1.1 /* right paren */) /* semicolon */; + +ALTER /* tablespace */ TABLESPACE /* name */ commented_reset /* action */ RESET /* left paren */ (/* option */ seq_page_cost, /* second option */ random_page_cost /* right paren */) /* semicolon */; + +ALTER /* tablespace */ TABLESPACE /* old name */ commented_old_name /* rename */ RENAME /* to */ TO /* new name */ commented_new_name /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/alter_text_search_configuration.sql b/crates/squawk_fmt/tests/before/alter_text_search_configuration.sql new file mode 100644 index 00000000..1796cc4b --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_text_search_configuration.sql @@ -0,0 +1,15 @@ +ALTER /* text */ TEXT /* search */ SEARCH /* configuration */ CONFIGURATION /* name */ public.english /* add */ ADD /* mapping */ MAPPING /* for */ FOR /* kind one */ asciiword /* kind comma */, /* kind two */ asciihword /* with */ WITH /* dictionary one */ public.simple /* dictionary comma */, /* dictionary two */ public.english_stem /* end */; + +ALTER TEXT SEARCH CONFIGURATION public.english ALTER /* mapping */ MAPPING /* for */ FOR /* kind */ word /* with */ WITH /* dictionary */ public.simple; + +ALTER TEXT SEARCH CONFIGURATION public.english ALTER MAPPING FOR asciiword, word REPLACE /* old dictionary */ public.old_dictionary /* with */ WITH /* new dictionary */ archive.new_dictionary; + +ALTER TEXT SEARCH CONFIGURATION public.english ALTER MAPPING /* replace */ REPLACE /* old */ public.old_dictionary /* with */ WITH /* new */ public.new_dictionary; + +ALTER TEXT SEARCH CONFIGURATION public.english DROP /* mapping */ MAPPING /* if */ IF /* exists */ EXISTS /* for */ FOR /* kind */ url /* comma */, /* second kind */ host; + +ALTER TEXT SEARCH CONFIGURATION public.english OWNER /* to */ TO /* role */ application_owner; + +ALTER TEXT SEARCH CONFIGURATION public.english SET /* schema */ SCHEMA /* schema name */ archive; + +ALTER TEXT SEARCH CONFIGURATION public.english RENAME /* to */ TO /* new name */ english_configuration_for_archived_documents_and_historical_search_results; diff --git a/crates/squawk_fmt/tests/before/alter_text_search_dictionary.sql b/crates/squawk_fmt/tests/before/alter_text_search_dictionary.sql new file mode 100644 index 00000000..6e70966c --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_text_search_dictionary.sql @@ -0,0 +1,9 @@ +ALTER /* text */ TEXT /* search */ SEARCH /* dictionary */ DICTIONARY /* name */ public.english /* left paren */ (/* first option */ stopwords /* equals */ = /* first value */ english /* comma */, /* second option */ language /* second equals */ = /* second value */ 'english' /* right paren */) /* end */; + +ALTER TEXT SEARCH DICTIONARY public.english (stopwords = english, accept = false, long_dictionary_configuration_option_name = 'a very long dictionary configuration value for wrapping'); + +ALTER TEXT SEARCH DICTIONARY public.english OWNER /* to */ TO /* owner */ application_owner; + +ALTER TEXT SEARCH DICTIONARY public.english SET /* schema */ SCHEMA /* schema */ archive; + +ALTER TEXT SEARCH DICTIONARY public.english RENAME /* to */ TO /* name */ english_dictionary_for_archived_documents_and_historical_search_results; diff --git a/crates/squawk_fmt/tests/before/alter_text_search_parser.sql b/crates/squawk_fmt/tests/before/alter_text_search_parser.sql new file mode 100644 index 00000000..1c2aecbb --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_text_search_parser.sql @@ -0,0 +1,3 @@ +ALTER /* text */ TEXT /* search */ SEARCH /* parser */ PARSER /* name */ public.default_parser /* rename */ RENAME /* to */ TO /* new name */ parser_for_archived_documents_and_historical_search_results /* end */; + +ALTER TEXT SEARCH PARSER public.default_parser SET /* schema */ SCHEMA /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/before/alter_text_search_template.sql b/crates/squawk_fmt/tests/before/alter_text_search_template.sql new file mode 100644 index 00000000..2f65b1e5 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_text_search_template.sql @@ -0,0 +1,3 @@ +ALTER /* text */ TEXT /* search */ SEARCH /* template */ TEMPLATE /* name */ public.default_template /* rename */ RENAME /* to */ TO /* new name */ template_for_archived_documents_and_historical_search_results /* end */; + +ALTER TEXT SEARCH TEMPLATE public.default_template SET /* schema */ SCHEMA /* schema name */ archive; diff --git a/crates/squawk_fmt/tests/before/alter_trigger.sql b/crates/squawk_fmt/tests/before/alter_trigger.sql new file mode 100644 index 00000000..1b931b79 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_trigger.sql @@ -0,0 +1,5 @@ +alter /* trigger */ trigger /* name */ audit_trigger /* on */ on /* relation */ public.accounts /* action */ no /* depends */ depends /* on extension */ on /* extension */ extension /* extension name */ audit_support /* end */; + +alter trigger audit_trigger on public.accounts depends on extension exceptionally_long_auditing_extension_name_for_regulated_customer_accounts; + +alter trigger audit_trigger on public.accounts rename /* to */ to /* target */ renamed_audit_trigger; diff --git a/crates/squawk_fmt/tests/before/alter_type.sql b/crates/squawk_fmt/tests/before/alter_type.sql new file mode 100644 index 00000000..1d14e201 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_type.sql @@ -0,0 +1,19 @@ +alter /* type */ type /* name */ public.delivery_status /* action */ add /* value */ value /* if */ if /* not */ not /* exists */ exists /* new value */ 'an exceptionally long delivery status awaiting additional customer confirmation' /* before */ before /* existing value */ 'delivered' /* end */; + +alter type public.delivery_status add value 'returned' after 'delivered'; + +alter type public.delivery_status rename /* value */ value /* old */ 'awaiting' /* to */ to /* new */ 'pending'; + +alter type public.customer_address add /* attribute */ attribute /* name */ street_name /* type */ character varying(255) /* collate */ collate /* collation */ "C" /* behavior */ cascade, /* next */ alter /* attribute */ attribute /* field */ postal_code /* set */ set /* data */ data /* type */ type /* new type */ character varying(32) /* behavior */ restrict, /* next */ drop /* attribute */ attribute /* if */ if /* exists */ exists /* field */ obsolete_field /* behavior */ cascade; + +alter type public.customer_address alter attribute country_code type text collate "C"; + +alter type public.customer_address rename /* attribute */ attribute /* old */ street_name /* to */ to /* new */ address_line /* behavior */ cascade; + +alter type public.customer_address owner /* to */ to /* owner */ current_user; + +alter type public.customer_address set /* options */ (/* receive */ receive = /* function */ public.receive_customer_address, /* send */ send = public.send_customer_address); + +alter type public.customer_address set /* schema */ schema /* target */ archived_customer_data; + +alter type public.customer_address rename /* to */ to /* target */ archived_customer_address; diff --git a/crates/squawk_fmt/tests/before/alter_user.sql b/crates/squawk_fmt/tests/before/alter_user.sql new file mode 100644 index 00000000..083d0dda --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_user.sql @@ -0,0 +1,7 @@ +alter /* user */ user /* role */ reporting_application_user /* in */ in /* database */ database /* name */ exceptionally_long_analytics_database_name /* action */ set /* parameter */ work_mem /* assignment */ to /* value */ '128MB' /* end */; + +alter user reporting_application_user with superuser createdb createrole login replication bypassrls connection limit 100 encrypted password 'an-exceptionally-long-secret-value' valid until '2042-02-22'; + +alter user reporting_application_user rename /* to */ to /* target */ renamed_reporting_application_user; + +alter user all in database analytics reset /* all */ all; diff --git a/crates/squawk_fmt/tests/before/alter_user_mapping.sql b/crates/squawk_fmt/tests/before/alter_user_mapping.sql new file mode 100644 index 00000000..1155b634 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_user_mapping.sql @@ -0,0 +1,3 @@ +alter /* user */ user /* mapping */ mapping /* for */ for /* role */ current_user /* server */ server /* server name */ exceptionally_long_foreign_data_server_for_customer_analytics /* options */ options /* left */ (/* add */ add /* option */ authentication_token /* value */ 'an-exceptionally-long-authentication-token-value', /* set */ set /* option */ endpoint /* value */ 'https://analytics.example.test/foreign-data', /* drop */ drop /* option */ obsolete_setting /* right */) /* end */; + +alter user mapping for public server short_server options (set user_name 'reporter'); diff --git a/crates/squawk_fmt/tests/before/alter_view.sql b/crates/squawk_fmt/tests/before/alter_view.sql new file mode 100644 index 00000000..2ccb2fc9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/alter_view.sql @@ -0,0 +1,17 @@ +alter view public.orders alter column status set default 'pending'; + +alter view if exists public.orders alter column status drop default; + +alter view public.orders rename column customer_identifier to account_identifier; + +alter view public.orders owner to reporting_role; + +alter view public.orders set schema reporting; + +alter view public.orders rename to archived_orders; + +alter view public.orders set (security_barrier = true, security_invoker = false); + +alter view public.orders reset (security_barrier, security_invoker); + +alter /* view keyword */ view /* if exists */ if /* exists keyword */ exists /* view name */ extraordinarily_long_schema_name.extraordinarily_long_view_name /* action */ alter /* column keyword */ column /* column name */ extraordinarily_long_column_name /* set keyword */ set /* default keyword */ default /* expression */ coalesce(current_setting('app.default_status'), 'pending') /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/comment_on.sql b/crates/squawk_fmt/tests/before/comment_on.sql new file mode 100644 index 00000000..0a0e9f73 --- /dev/null +++ b/crates/squawk_fmt/tests/before/comment_on.sql @@ -0,0 +1,23 @@ +comment on table public.orders is 'customer orders'; + +comment on column public.orders.customer_identifier is 'external customer identifier'; + +comment on constraint positive_amount on domain public.positive_money is 'amount must be positive'; + +comment on policy tenant_isolation on extraordinarily_long_schema_name.extraordinarily_long_table_name is 'restrict rows to the current tenant'; + +comment on rule refresh_summary on public.orders is null; + +comment on trigger update_timestamp on public.orders is 'maintains updated_at'; + +comment on large object 123456789 is 'archived document'; + +comment on operator public.+(integer, integer) is 'integer addition'; + +comment on operator class public.text_ops using btree is 'text operator class'; + +comment on transform for jsonb language plpgsql is 'json transform'; + +comment on property graph public.sales_graph is 'sales relationships'; + +comment /* on keyword */ on /* object kind */ materialized /* view keyword */ view /* object name */ extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name /* is keyword */ is /* comment value */ 'A deliberately long description of the materialized view used to verify wrapping behavior.' /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_access_method.sql b/crates/squawk_fmt/tests/before/create_access_method.sql new file mode 100644 index 00000000..5a329234 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_access_method.sql @@ -0,0 +1,5 @@ +create access method heap2 type table handler public.heap2_handler; + +create access method extraordinarily_long_access_method_name type index handler extraordinarily_long_schema_name.extraordinarily_long_handler_function_name; + +create /* access keyword */ access /* method keyword */ method /* name */ commented_method /* type keyword */ type /* method type */ index /* handler keyword */ handler /* handler name */ public.commented_handler /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_aggregate.sql b/crates/squawk_fmt/tests/before/create_aggregate.sql new file mode 100644 index 00000000..be41a3f9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_aggregate.sql @@ -0,0 +1,13 @@ +create aggregate public.sum_of_squares(integer) (sfunc = public.sum_squares_state, stype = bigint, initcond = '0'); + +create or replace aggregate public.count_any(*) (sfunc = public.count_state, stype = bigint); + +create aggregate public.old_style_sum (sfunc = public.sum_state, basetype = integer, stype = bigint); + +create or replace aggregate extraordinarily_long_schema_name.extraordinarily_long_aggregate_name(double precision, numeric, bigint) (sfunc = extraordinarily_long_schema_name.extraordinarily_long_transition_function_name, stype = extraordinarily_long_schema_name.extraordinarily_long_state_type_name, parallel = safe); + +create /* or replace */ or /* replace keyword */ replace /* aggregate keyword */ aggregate /* name */ public.commented_aggregate(/* parameter */ integer) /* attributes */ (/* first attribute */ sfunc /* equals */ = /* value */ public.commented_state, /* second attribute */ stype = /* type value */ bigint /* closing parenthesis */) /* semicolon */; + +create aggregate public.ordered_set_aggregate(/* direct parameter */ double precision /* order keyword */ order /* by keyword */ by /* first ordered parameter */ anyelement, /* second ordered parameter */ text /* closing parenthesis */) (sfunc = public.ordered_set_state, stype = internal); + +create aggregate extraordinarily_long_schema_name.extraordinarily_long_ordered_set_aggregate_name(extraordinarily_long_schema_name.extraordinarily_long_direct_argument_type order by extraordinarily_long_schema_name.extraordinarily_long_first_ordered_argument_type, extraordinarily_long_schema_name.extraordinarily_long_second_ordered_argument_type) (sfunc = extraordinarily_long_schema_name.extraordinarily_long_transition_function_name, stype = internal); diff --git a/crates/squawk_fmt/tests/before/create_cast.sql b/crates/squawk_fmt/tests/before/create_cast.sql new file mode 100644 index 00000000..40ea221a --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_cast.sql @@ -0,0 +1,13 @@ +create cast (integer as bigint) with function public.integer_to_bigint(integer) as assignment; + +create cast (text as varchar) without function as implicit; + +create cast (json as jsonb) with inout; + +create cast (extraordinarily_long_schema_name.extraordinarily_long_source_type_name as extraordinarily_long_schema_name.extraordinarily_long_target_type_name) with function extraordinarily_long_schema_name.extraordinarily_long_conversion_function_name(extraordinarily_long_schema_name.extraordinarily_long_source_type_name) as implicit; + +create /* cast keyword */ cast /* left parenthesis */ (/* source type */ text /* as keyword */ as /* target type */ varchar /* right parenthesis */) /* method */ with /* function keyword */ function /* function name */ public.convert_text(/* argument type */ text /* argument closing parenthesis */) /* context */ as /* assignment keyword */ assignment /* semicolon */; + +create cast (integer as text) /* method */ without /* function keyword */ function /* context */ as /* implicit keyword */ implicit; + +create cast (xml as text) /* method */ with /* inout keyword */ inout; diff --git a/crates/squawk_fmt/tests/before/create_collation.sql b/crates/squawk_fmt/tests/before/create_collation.sql new file mode 100644 index 00000000..96db4b19 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_collation.sql @@ -0,0 +1,9 @@ +create collation public.english (locale = 'en_US.UTF-8', provider = icu, deterministic = false); + +create collation if not exists public.english_copy from public.english; + +create collation extraordinarily_long_schema_name.extraordinarily_long_collation_name (locale = 'en-US-u-va-posix', provider = icu, rules = '&V << w <<< W', version = '153.120'); + +create /* collation keyword */ collation /* if keyword */ if /* not keyword */ not /* exists keyword */ exists /* name */ public.commented /* left parenthesis */ (/* first option */ locale /* equals */ = /* value */ 'en_US.UTF-8' /* comma */, /* second option */ provider = /* provider value */ icu /* closing parenthesis */) /* semicolon */; + +create collation /* name */ public.commented_copy /* from clause */ from /* source collation */ public.original /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_conversion.sql b/crates/squawk_fmt/tests/before/create_conversion.sql new file mode 100644 index 00000000..8f20ce48 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_conversion.sql @@ -0,0 +1,7 @@ +create conversion public.latin_to_utf8 for 'LATIN1' to 'UTF8' from public.latin1_to_utf8; + +create default conversion public.default_latin_to_utf8 for 'LATIN1' to 'UTF8' from public.latin1_to_utf8; + +create conversion extraordinarily_long_schema_name.extraordinarily_long_conversion_name for 'EXTRAORDINARILY_LONG_SOURCE_ENCODING' to 'EXTRAORDINARILY_LONG_DESTINATION_ENCODING' from extraordinarily_long_schema_name.extraordinarily_long_conversion_function_name; + +create /* default keyword */ default /* conversion keyword */ conversion /* name */ public.commented_conversion /* for keyword */ for /* source encoding */ 'LATIN1' /* to keyword */ to /* destination encoding */ 'UTF8' /* from keyword */ from /* function */ public.convert_encoding /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_database.sql b/crates/squawk_fmt/tests/before/create_database.sql new file mode 100644 index 00000000..2f2051b9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_database.sql @@ -0,0 +1,7 @@ +create database app; + +create database app_copy with owner = app_owner template = template0 encoding = 'UTF8' locale = 'en_US.UTF-8' tablespace = app_space connection limit = 100; + +create database extraordinarily_long_database_name with owner = extraordinarily_long_database_owner template = extraordinarily_long_template_database encoding = 'UTF8' strategy = wal_log locale_provider = icu icu_locale = 'en-US-u-va-posix' collation_version = '153.120' tablespace = extraordinarily_long_tablespace_name allow_connections = true connection limit = 250 is_template = false oid = 16384; + +create /* database keyword */ database /* database name */ commented_database /* with keyword */ with /* owner option */ owner /* equals */ = /* owner value */ commented_owner /* template option */ template = /* template value */ template0 /* encoding option */ encoding = /* encoding value */ 'UTF8' /* generic option */ locale /* generic equals */ = /* generic value */ 'en_US.UTF-8' /* connection option */ connection /* limit keyword */ limit = /* limit value */ 42 /* tablespace option */ tablespace = /* tablespace value */ commented_space /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_domain.sql b/crates/squawk_fmt/tests/before/create_domain.sql new file mode 100644 index 00000000..e8c01b02 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_domain.sql @@ -0,0 +1,5 @@ +create domain public.email_address as varchar(255) collate "C" default 'unknown@example.com' constraint email_address_valid check (value like '%@%') not null; + +create domain extraordinarily_long_schema_name.extraordinarily_long_domain_name as character varying(1024) collate extraordinarily_long_schema_name.extraordinarily_long_collation_name default 'an extraordinarily long default value that forces wrapping' check (length(value) > 3) not null; + +/* before create */ create /* before domain */ domain /* before name */ public.commented_domain /* before as */ as /* before type */ text /* before collate */ collate /* before collation */ "C" /* before constraint */ constraint /* before constraint name */ valid_value /* before check */ check /* before left paren */ (/* before expression */ value <> '' /* before right paren */) /* before not */ not /* before null */ null /* before semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_event_trigger.sql b/crates/squawk_fmt/tests/before/create_event_trigger.sql new file mode 100644 index 00000000..ad74d035 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_event_trigger.sql @@ -0,0 +1,5 @@ +create event trigger ddl_watch on ddl_command_start execute function audit.log_ddl(); + +create event trigger extraordinarily_long_event_trigger_name on ddl_command_end when tag in ('CREATE TABLE', 'ALTER TABLE', 'CREATE MATERIALIZED VIEW') and extension in ('extraordinarily_long_extension_name') execute procedure extraordinarily_long_schema_name.extraordinarily_long_event_trigger_procedure(); + +/* before create */ create /* before event */ event /* before trigger */ trigger /* before name */ commented_trigger /* before on */ on /* before event name */ ddl_command_end /* before when */ when /* before variable */ tag /* before in */ in /* before left paren */ (/* before first literal */ 'CREATE TABLE' /* before comma */, /* after comma */ 'ALTER TABLE' /* before right paren */) /* before and */ and /* before second variable */ extension /* before second in */ in /* before second left paren */ (/* before second literal */ 'postgis' /* before second right paren */) /* before execute */ execute /* before function */ function /* before call */ audit.log_ddl /* before call left paren */ (/* before call right paren */) /* before semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_extension.sql b/crates/squawk_fmt/tests/before/create_extension.sql new file mode 100644 index 00000000..bcfc0ef9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_extension.sql @@ -0,0 +1,7 @@ +create extension postgis; + +create extension if not exists postgis with schema extensions version '3.4.2' cascade; + +create extension if not exists extraordinarily_long_extension_name with schema extraordinarily_long_schema_name version 'extraordinarily-long-extension-version-number' cascade; + +/* before create */ create /* before extension */ extension /* before if */ if /* before not */ not /* before exists */ exists /* before name */ commented_extension /* before with */ with /* before schema */ schema /* before schema name */ extensions /* before version */ version /* before version value */ '1.2.3' /* before cascade */ cascade /* before semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_foreign_data_wrapper.sql b/crates/squawk_fmt/tests/before/create_foreign_data_wrapper.sql new file mode 100644 index 00000000..3e54f272 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_foreign_data_wrapper.sql @@ -0,0 +1,7 @@ +create foreign data wrapper local_fdw no handler no validator; + +create foreign data wrapper postgres_fdw handler public.postgres_fdw_handler validator public.postgres_fdw_validator options (host 'localhost', port '5432'); + +create foreign data wrapper extraordinarily_long_foreign_data_wrapper_name handler extraordinarily_long_schema_name.extraordinarily_long_handler_function validator extraordinarily_long_schema_name.extraordinarily_long_validator_function options (extraordinarily_long_option_name 'extraordinarily long foreign data wrapper option value'); + +/* before create */ create /* before foreign */ foreign /* before data */ data /* before wrapper */ wrapper /* before name */ commented_fdw /* before handler */ handler /* before handler name */ public.handler_fn /* before validator */ validator /* before validator name */ public.validator_fn /* before options */ options /* before left paren */ (/* before option name */ host /* before option value */ 'localhost' /* before comma */, /* after comma */ port /* before second value */ '5432' /* before right paren */) /* before semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_group.sql b/crates/squawk_fmt/tests/before/create_group.sql new file mode 100644 index 00000000..2754e25c --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_group.sql @@ -0,0 +1,6 @@ +create group analysts; + +create group extraordinarily_long_group_name_for_business_intelligence_and_reporting with superuser createdb createrole inherit login replication bypassrls connection limit 250 encrypted password 'an extraordinarily long password value used to exercise formatter wrapping' valid until '2042-02-22' in role analysts, developers, administrators admin current_user, session_user; + +-- comments in every position +create /* group */ group /* name */ commented_group /* options */ with /* generic option */ superuser /* connection */ connection /* limit */ limit /* number */ 100 /* in */ in /* role */ role /* first role */ foo /* comma */, /* second role */ bar /* admin */ admin /* admin role */ current_user /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_language.sql b/crates/squawk_fmt/tests/before/create_language.sql new file mode 100644 index 00000000..45099a0f --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_language.sql @@ -0,0 +1,6 @@ +create language plsample; + +create or replace trusted procedural language extraordinarily_long_procedural_language_name handler extraordinarily_long_schema_name.extraordinarily_long_handler_function_name inline extraordinarily_long_schema_name.extraordinarily_long_inline_function_name validator extraordinarily_long_schema_name.extraordinarily_long_validator_function_name; + +-- comments in every position +create /* or */ or /* replace */ replace /* trusted */ trusted /* procedural */ procedural /* language */ language /* name */ commented_language /* handler */ handler /* handler schema */ handlers /* dot */ . /* handler function */ call_handler /* inline */ inline /* inline function */ handlers.call_inline /* validator */ validator /* validator function */ handlers.call_validator /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_materialized_view.sql b/crates/squawk_fmt/tests/before/create_materialized_view.sql new file mode 100644 index 00000000..af9595bb --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_materialized_view.sql @@ -0,0 +1,6 @@ +create materialized view active_accounts as select account_id, account_name from accounts where active = true; + +create materialized view if not exists extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name (extraordinarily_long_account_identifier, extraordinarily_long_account_display_name) using extraordinarily_long_access_method_name with (fillfactor = 80, autovacuum_enabled = true) tablespace extraordinarily_long_tablespace_name as select extraordinarily_long_account_identifier, extraordinarily_long_account_display_name from extraordinarily_long_schema_name.extraordinarily_long_accounts_table with no data; + +-- comments in every position +create /* materialized */ materialized /* view */ view /* if */ if /* not */ not /* exists */ exists /* schema */ app /* dot */ . /* name */ account_rollup /* columns */ (/* first column */ account_id /* comma */, /* second column */ total /* close columns */) /* using */ using /* method */ heap /* with */ with /* attributes */ (/* attribute */ fillfactor /* equals */ = /* value */ 90 /* close attributes */) /* tablespace */ tablespace /* tablespace name */ fastspace /* as */ as /* query */ select /* expression */ account_id, sum(amount) from payments group by account_id /* with */ with /* no */ no /* data */ data /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_operator.sql b/crates/squawk_fmt/tests/before/create_operator.sql new file mode 100644 index 00000000..bcb9480a --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_operator.sql @@ -0,0 +1,6 @@ +create operator > (function = greater_than); + +create operator extraordinarily_long_schema_name.## (function = extraordinarily_long_schema_name.extraordinarily_long_operator_function_name, leftarg = extraordinarily_long_schema_name.extraordinarily_long_left_argument_type, rightarg = extraordinarily_long_schema_name.extraordinarily_long_right_argument_type, commutator = &&&&, hashes, merges); + +-- comments in every position +create /* operator */ operator /* schema */ app /* dot */ . /* symbol */ # /* attributes */ (/* function name */ function /* equals */ = /* function value */ app.compare_values /* comma */, /* left argument */ leftarg /* second equals */ = /* type */ integer /* comma */, /* marker */ hashes /* closing parenthesis */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_operator_class.sql b/crates/squawk_fmt/tests/before/create_operator_class.sql new file mode 100644 index 00000000..690eacb7 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_operator_class.sql @@ -0,0 +1,6 @@ +create operator class integer_minmax_ops default for type integer using btree as operator 1 < for search, function 1 (integer, integer) int4cmp(integer, integer), storage integer; + +create operator class extraordinarily_long_schema_name.extraordinarily_long_integer_operator_class default for type extraordinarily_long_schema_name.extraordinarily_long_integer_type using extraordinarily_long_access_method family extraordinarily_long_schema_name.extraordinarily_long_operator_family as operator 1 < (extraordinarily_long_schema_name.extraordinarily_long_integer_type, extraordinarily_long_schema_name.extraordinarily_long_integer_type) for order by extraordinarily_long_schema_name.extraordinarily_long_sort_family, function 1 (extraordinarily_long_schema_name.extraordinarily_long_integer_type, extraordinarily_long_schema_name.extraordinarily_long_integer_type) extraordinarily_long_schema_name.extraordinarily_long_comparison_function(extraordinarily_long_schema_name.extraordinarily_long_integer_type, extraordinarily_long_schema_name.extraordinarily_long_integer_type), storage extraordinarily_long_schema_name.extraordinarily_long_storage_type; + +-- comments in every position +create /* operator */ operator /* class */ class /* schema */ app /* dot */ . /* name */ integer_ops /* default */ default /* for */ for /* type keyword */ type /* data type */ integer /* using */ using /* method */ btree /* family */ family /* family schema */ app /* family dot */ . /* family name */ integer_family /* as */ as /* option */ operator /* number */ 1 /* operator name */ < /* left paren */ (/* left type */ integer /* type comma */, /* right type */ integer /* right paren */) /* purpose for */ for /* purpose search */ search /* option comma */, /* function */ function /* function number */ 1 /* params */ (/* first param */ integer /* param comma */, /* second param */ integer /* params close */) /* function signature */ app.compare_integers(/* signature arg */ integer, integer) /* second comma */, /* storage */ storage /* storage type */ integer /* end */; diff --git a/crates/squawk_fmt/tests/before/create_operator_family.sql b/crates/squawk_fmt/tests/before/create_operator_family.sql new file mode 100644 index 00000000..d4b9c6c7 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_operator_family.sql @@ -0,0 +1,6 @@ +create operator family integer_ops using btree; + +create operator family extraordinarily_long_schema_name.extraordinarily_long_operator_family_name using extraordinarily_long_custom_index_access_method; + +-- comments in every position +create /* operator */ operator /* family */ family /* schema */ app /* dot */ . /* name */ integer_family /* using */ using /* method */ btree /* end */; diff --git a/crates/squawk_fmt/tests/before/create_policy.sql b/crates/squawk_fmt/tests/before/create_policy.sql new file mode 100644 index 00000000..67300b5f --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_policy.sql @@ -0,0 +1,6 @@ +create policy account_access on public.accounts; + +create policy extraordinarily_long_account_access_policy_name on extraordinarily_long_schema_name.extraordinarily_long_accounts_table_name as restrictive for select to extraordinarily_long_account_administrator_role, extraordinarily_long_account_auditor_role using (account_owner_identifier = current_user and account_identifier > 1000000) with check (account_is_active and account_owner_identifier = current_user); + +-- comments in every position +create /* policy */ policy /* name */ account_access /* on */ on /* schema */ public /* dot */ . /* table */ accounts /* as */ as /* policy type */ restrictive /* for */ for /* command */ select /* to */ to /* first role */ account_admin /* comma */, /* second role */ account_auditor /* using */ using /* left paren */ (/* expression */ owner_name = current_user /* right paren */) /* with */ with /* check */ check /* check left paren */ (/* check expression */ active /* check right paren */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_procedure.sql b/crates/squawk_fmt/tests/before/create_procedure.sql new file mode 100644 index 00000000..de5ea873 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_procedure.sql @@ -0,0 +1,6 @@ +create procedure refresh_accounts() language sql as $$ select 1 $$; + +create or replace procedure extraordinarily_long_schema_name.extraordinarily_long_account_recalculation_procedure(in extraordinarily_long_account_identifier integer, in extraordinarily_long_effective_timestamp timestamp with time zone, out extraordinarily_long_recalculated_total numeric) language plpgsql security definer set work_mem = '256MB' as $$ begin null; end $$; + +-- comments in every position +create /* or */ or /* replace */ replace /* procedure */ procedure /* schema */ app /* dot */ . /* name */ recalculate /* params */ (/* mode */ in /* parameter */ account_id /* type */ integer /* comma */, /* out mode */ out /* output */ result /* output type */ text /* close */) /* language */ language /* language name */ plpgsql /* security */ security /* definer */ definer /* set */ set /* parameter name */ work_mem /* equals */ = /* setting */ '64MB' /* as */ as /* body */ $$ begin null; end $$ /* end */; diff --git a/crates/squawk_fmt/tests/before/create_property_graph.sql b/crates/squawk_fmt/tests/before/create_property_graph.sql new file mode 100644 index 00000000..15cea54d --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_property_graph.sql @@ -0,0 +1,6 @@ +create property graph social vertex tables (people key (id)) edge tables (follows source key (follower_id) references people (id) destination key (followed_id) references people (id)); + +create unlogged property graph extraordinarily_long_social_relationship_property_graph vertex tables (extraordinarily_long_people_table key (extraordinarily_long_person_identifier) label person properties (display_name as public_name, date_of_birth), extraordinarily_long_organizations_table key (extraordinarily_long_organization_identifier) no properties) edge tables (extraordinarily_long_employment_relationship_table source key (employee_identifier) references extraordinarily_long_people_table (extraordinarily_long_person_identifier) destination key (employer_identifier) references extraordinarily_long_organizations_table (extraordinarily_long_organization_identifier) properties all columns); + +-- comments in every position +create /* persistence */ local /* temporary */ temporary /* property */ property /* graph */ graph /* graph name */ public /* dot */ . /* name */ social /* vertex */ vertex /* tables */ tables /* left paren */ (/* table */ people /* key */ key /* key left */ (/* key column */ id /* key right */) /* label */ label /* label name */ person /* properties */ properties /* properties left */ (/* expression */ full_name /* as */ as /* property name */ name /* properties right */) /* comma */, /* second table */ organizations /* no */ no /* properties */ properties /* vertex right */) /* edge */ edge /* edge tables */ tables /* edge left */ (/* edge table */ follows /* source */ source /* source key */ key /* source left */ (/* source column */ follower_id /* source right */) /* references */ references /* source table */ people /* source reference left */ (/* source reference column */ id /* source reference right */) /* destination */ destination /* destination key */ key /* destination left */ (/* destination column */ followed_id /* destination right */) /* destination references */ references /* destination table */ people /* destination reference left */ (/* destination reference column */ id /* destination reference right */) /* all properties */ properties /* all */ all /* columns */ columns /* edge right */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_role.sql b/crates/squawk_fmt/tests/before/create_role.sql new file mode 100644 index 00000000..b95662d2 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_role.sql @@ -0,0 +1,6 @@ +create role analyst; + +create role extraordinarily_long_reporting_and_data_warehouse_administrator with login encrypted password 'an-extraordinarily-long-secret-value' valid until '2035-12-31 23:59:59+00' connection limit 25 in role extraordinarily_long_read_only_reporting_role, extraordinarily_long_data_warehouse_role admin database_administrator; + +-- comments in every position +create /* role */ role /* name */ reporting_user /* with */ with /* login */ login /* encrypted */ encrypted /* password */ password /* secret */ 'secret' /* valid */ valid /* until */ until /* timestamp */ '2030-01-01' /* connection */ connection /* limit */ limit /* count */ 10 /* in */ in /* role option */ role /* first role */ readers /* comma */, /* second role */ writers /* admin */ admin /* admin role */ role_admin /* end */; diff --git a/crates/squawk_fmt/tests/before/create_rule.sql b/crates/squawk_fmt/tests/before/create_rule.sql new file mode 100644 index 00000000..468081a9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_rule.sql @@ -0,0 +1,6 @@ +create rule ignore_delete as on delete to public.accounts do instead nothing; + +create or replace rule extraordinarily_long_rule_name_for_recording_all_account_changes as on update to extraordinarily_long_schema_name.extraordinarily_long_accounts_table_name where (old.account_owner_identifier is distinct from new.account_owner_identifier and new.account_is_active) do also (notify extraordinarily_long_account_changes_channel, 'account owner changed'; update extraordinarily_long_audit_table_name set account_was_changed = true where account_identifier = new.account_identifier;); + +-- comments in every position +create /* or */ or /* replace */ replace /* rule */ rule /* name */ audit_change /* as */ as /* on */ on /* event */ update /* to */ to /* schema */ public /* dot */ . /* relation */ accounts /* where */ where /* left paren */ (/* condition */ old.owner_id is distinct from new.owner_id /* right paren */) /* do */ do /* instead */ instead /* action left */ (/* notify */ notify /* channel */ account_changes /* comma */, /* payload */ 'changed' /* first semicolon */; /* update */ update /* table */ audit_log /* set */ set /* column */ changed /* equals */ = /* value */ true /* second semicolon */; /* action right */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_schema.sql b/crates/squawk_fmt/tests/before/create_schema.sql new file mode 100644 index 00000000..57866c93 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_schema.sql @@ -0,0 +1,10 @@ +create schema app; + +create schema if not exists extraordinarily_long_application_reporting_and_data_warehouse_schema authorization extraordinarily_long_application_reporting_and_data_warehouse_owner_role; + +create schema authorization current_user; + +-- comments in every position +create /* schema */ schema /* if */ if /* not */ not /* exists */ exists /* schema name */ reporting /* authorization */ authorization /* role */ reporting_owner /* end */; + +create /* schema */ schema /* authorization */ authorization /* role */ schema_owner /* end */; diff --git a/crates/squawk_fmt/tests/before/create_sequence.sql b/crates/squawk_fmt/tests/before/create_sequence.sql new file mode 100644 index 00000000..95930d69 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_sequence.sql @@ -0,0 +1,6 @@ +create sequence invoice_number_seq; + +create temporary sequence if not exists extraordinarily_long_schema_name.extraordinarily_long_invoice_number_sequence as bigint increment by 5 minvalue 100 maxvalue 999999999 start with 100 cache 50 cycle owned by extraordinarily_long_schema_name.extraordinarily_long_table_name.extraordinarily_long_column_name; + +-- comments in every position +create /* temporary */ temporary /* sequence */ sequence /* if */ if /* not */ not /* exists */ exists /* name */ app.order_seq /* as */ as /* type */ bigint /* increment */ increment /* by */ by /* increment value */ 2 /* no */ no /* minvalue */ minvalue /* maxvalue */ maxvalue /* max */ 1000 /* start */ start /* with */ with /* start value */ 10 /* cache */ cache /* cache value */ 20 /* no cycle */ no /* cycle */ cycle /* owned */ owned /* by */ by /* owner */ app.orders.id /* end */; diff --git a/crates/squawk_fmt/tests/before/create_server.sql b/crates/squawk_fmt/tests/before/create_server.sql new file mode 100644 index 00000000..a27295ee --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_server.sql @@ -0,0 +1,6 @@ +create server analytics foreign data wrapper postgres_fdw; + +create server if not exists extraordinarily_long_analytics_foreign_server type 'extraordinarily long server type description' version 'PostgreSQL version 17.2 with additional compatibility information' foreign data wrapper extraordinarily_long_foreign_data_wrapper_name options (host 'analytics.example.internal', dbname 'extraordinarily_long_analytics_database_name', port '5432'); + +-- comments in every position +create /* server */ server /* if */ if /* not */ not /* exists */ exists /* name */ reporting /* type */ type /* type value */ 'postgresql' /* version */ version /* version value */ '17' /* foreign */ foreign /* data */ data /* wrapper */ wrapper /* fdw */ postgres_fdw /* options */ options /* open */ (/* option name */ host /* option value */ 'localhost' /* comma */, /* second name */ port /* second value */ '5432' /* close */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_statistics.sql b/crates/squawk_fmt/tests/before/create_statistics.sql new file mode 100644 index 00000000..692e5ea4 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_statistics.sql @@ -0,0 +1,6 @@ +create statistics user_activity_stats on user_id, created_at from app.user_activity; + +create statistics if not exists extraordinarily_long_schema_name.extraordinarily_long_statistics_name (ndistinct, dependencies, mcv) on extraordinarily_long_first_column_name, (lower(extraordinarily_long_second_column_name)), extraordinarily_long_third_column_name + extraordinarily_long_fourth_column_name from extraordinarily_long_schema_name.extraordinarily_long_table_name; + +-- comments in every position +create /* statistics */ statistics /* if */ if /* not */ not /* exists */ exists /* name */ app.orders_stats /* open kinds */ (/* first kind */ ndistinct /* kind comma */, /* second kind */ dependencies /* close kinds */) /* on */ on /* first expression */ customer_id /* expression comma */, /* second expression */ (lower(email)) /* from */ from /* table */ app.orders /* end */; diff --git a/crates/squawk_fmt/tests/before/create_tablespace.sql b/crates/squawk_fmt/tests/before/create_tablespace.sql new file mode 100644 index 00000000..b2535c40 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_tablespace.sql @@ -0,0 +1,6 @@ +create tablespace fastspace location '/data/fastspace'; + +create tablespace extraordinarily_long_tablespace_name_for_historical_analytics owner extraordinarily_long_database_owner_role location '/srv/postgresql/extraordinarily/long/location/for/historical/analytics/tablespace' with (random_page_cost = 1.1, seq_page_cost = 0.9, effective_io_concurrency = 200); + +-- comments in every position +create /* tablespace */ tablespace /* name */ reporting_space /* owner */ owner /* role */ reporting_owner /* location */ location /* path */ '/srv/reporting' /* with */ with /* open */ (/* first name */ random_page_cost /* equals */ = /* first value */ 1.2 /* comma */, /* second name */ seq_page_cost /* second equals */ = /* second value */ 1.0 /* close */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_text_search_configuration.sql b/crates/squawk_fmt/tests/before/create_text_search_configuration.sql new file mode 100644 index 00000000..c36031f6 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_text_search_configuration.sql @@ -0,0 +1,5 @@ +create text search configuration public.english (parser = public.default_parser); + +create text search configuration extraordinarily_long_schema_name.extraordinarily_long_configuration_name (copy = public.extraordinarily_long_source_configuration_name); + +create /* text keyword */ text /* search keyword */ search /* configuration keyword */ configuration /* name */ public /* dot */ . /* name segment */ commented_configuration /* left parenthesis */ (/* first option */ parser /* equals */ = /* value */ public.default_parser /* comma */, /* second option */ copy = /* second value */ public.english /* right parenthesis */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_text_search_dictionary.sql b/crates/squawk_fmt/tests/before/create_text_search_dictionary.sql new file mode 100644 index 00000000..bfa4ecf0 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_text_search_dictionary.sql @@ -0,0 +1,5 @@ +create text search dictionary public.english_stem (template = snowball, language = english); + +create text search dictionary extraordinarily_long_schema_name.extraordinarily_long_dictionary_name (template = public.extraordinarily_long_dictionary_template_name, stopwords = 'extraordinarily_long_stopword_dictionary_name'); + +create /* text keyword */ text /* search keyword */ search /* dictionary keyword */ dictionary /* name */ public /* dot */ . /* name segment */ commented_dictionary /* left parenthesis */ (/* first option */ template /* equals */ = /* value */ public.snowball /* comma */, /* second option */ language = /* second value */ english /* right parenthesis */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_text_search_parser.sql b/crates/squawk_fmt/tests/before/create_text_search_parser.sql new file mode 100644 index 00000000..1fb8cd39 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_text_search_parser.sql @@ -0,0 +1,5 @@ +create text search parser public.default_parser (start = public.parser_start, gettoken = public.parser_gettoken, end = public.parser_end, lextypes = public.parser_lextypes); + +create text search parser extraordinarily_long_schema_name.extraordinarily_long_parser_name (start = public.extraordinarily_long_parser_start_function_name, gettoken = public.extraordinarily_long_parser_gettoken_function_name, end = public.extraordinarily_long_parser_end_function_name, lextypes = public.extraordinarily_long_parser_lextypes_function_name, headline = public.extraordinarily_long_parser_headline_function_name); + +create /* text keyword */ text /* search keyword */ search /* parser keyword */ parser /* name */ public /* dot */ . /* name segment */ commented_parser /* left parenthesis */ (/* first option */ start /* equals */ = /* value */ public.parser_start /* comma */, /* second option */ gettoken = /* second value */ public.parser_gettoken /* comma two */, /* third option */ end = public.parser_end, lextypes = public.parser_lextypes /* right parenthesis */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_text_search_template.sql b/crates/squawk_fmt/tests/before/create_text_search_template.sql new file mode 100644 index 00000000..ecd0ef03 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_text_search_template.sql @@ -0,0 +1,5 @@ +create text search template public.simple_template (lexize = public.simple_lexize); + +create text search template extraordinarily_long_schema_name.extraordinarily_long_template_name (init = public.extraordinarily_long_template_initialization_function_name, lexize = public.extraordinarily_long_template_lexize_function_name); + +create /* text keyword */ text /* search keyword */ search /* template keyword */ template /* name */ public /* dot */ . /* name segment */ commented_template /* left parenthesis */ (/* first option */ init /* equals */ = /* value */ public.simple_init /* comma */, /* second option */ lexize = /* second value */ public.simple_lexize /* right parenthesis */) /* semicolon */; diff --git a/crates/squawk_fmt/tests/before/create_type.sql b/crates/squawk_fmt/tests/before/create_type.sql new file mode 100644 index 00000000..a49ee1f8 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_type.sql @@ -0,0 +1,20 @@ +create type shell_type; + +create type inventory_item as (name text, supplier_id integer, extraordinarily_long_product_description character varying(255) collate "C", price numeric); + +create type mood as enum ('sad', 'ok', 'happy', 'extraordinarily delighted and enthusiastic'); + +create type float8_range as range (subtype = float8, subtype_diff = float8mi); + +create type complex_base (input = complex_in, output = complex_out, internallength = 16); + +-- comments in every composite type position +create /* type */ type /* name */ commented_composite /* as */ as /* open */ (/* field */ first_field /* type */ text /* collate */ collate /* collation */ "C" /* comma */, /* next field */ second_field /* next type */ integer /* close */) /* end */; + +-- comments in every enum type position +create type /* enum name */ commented_enum /* as */ as /* enum */ enum /* open */ (/* first */ 'one' /* comma */, /* second */ 'two' /* close */) /* end */; + +-- comments in every range/base type position +create type /* range name */ commented_range /* as */ as /* range */ range /* attributes */ (/* option */ subtype /* equals */ = /* value */ numeric /* comma */, /* option two */ multirange_type_name = commented_multirange /* close */) /* end */; + +create type /* base name */ commented_base /* attributes */ (/* input */ input /* equals */ = /* value */ commented_in /* comma */, /* output */ output = commented_out /* close */) /* end */; diff --git a/crates/squawk_fmt/tests/before/create_user.sql b/crates/squawk_fmt/tests/before/create_user.sql new file mode 100644 index 00000000..630a7df9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_user.sql @@ -0,0 +1,6 @@ +create user app_user; + +create user extraordinarily_long_application_reporting_user with login encrypted password 'an-extraordinarily-long-secret-value' valid until '2035-12-31 23:59:59+00' connection limit 25 in role extraordinarily_long_read_only_reporting_role, extraordinarily_long_data_warehouse_role; + +-- comments in every position +create /* user */ user /* name */ reporting_user /* with */ with /* login */ login /* encrypted */ encrypted /* password */ password /* secret */ 'secret' /* valid */ valid /* until */ until /* timestamp */ '2030-01-01' /* connection */ connection /* limit */ limit /* count */ 10 /* in */ in /* role option */ role /* first role */ readers /* comma */, /* second role */ writers /* end */; diff --git a/crates/squawk_fmt/tests/before/create_user_mapping.sql b/crates/squawk_fmt/tests/before/create_user_mapping.sql new file mode 100644 index 00000000..44cb6e02 --- /dev/null +++ b/crates/squawk_fmt/tests/before/create_user_mapping.sql @@ -0,0 +1,6 @@ +create user mapping for app_user server app_server; + +create user mapping if not exists for extraordinarily_long_application_reporting_user server extraordinarily_long_foreign_data_server options (user 'extraordinarily_long_remote_user_name', password 'an-extraordinarily-long-secret-value'); + +-- comments in every position +create /* user */ user /* mapping */ mapping /* if */ if /* not */ not /* exists */ exists /* for */ for /* role */ current_user /* server */ server /* server name */ remote_server /* options */ options /* open */ (/* option */ user /* value */ 'remote_user' /* comma */, /* next option */ password /* next value */ 'secret' /* close */) /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_access_method.sql b/crates/squawk_fmt/tests/before/drop_access_method.sql new file mode 100644 index 00000000..c82141a4 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_access_method.sql @@ -0,0 +1,6 @@ +drop access method heap2; + +drop access method if exists extraordinarily_long_custom_table_access_method_name cascade; + +-- comments in every position +drop /* access */ access /* method */ method /* if */ if /* exists */ exists /* name */ custom_method /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_aggregate.sql b/crates/squawk_fmt/tests/before/drop_aggregate.sql new file mode 100644 index 00000000..0773c5c8 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_aggregate.sql @@ -0,0 +1,8 @@ +drop aggregate public.sum(integer); + +drop aggregate if exists extraordinarily_long_schema_name.extraordinarily_long_aggregate_name(extraordinarily_long_schema_name.extraordinarily_long_argument_type_name), public.another_extraordinarily_long_aggregate_name(double precision, numeric) cascade; + +drop aggregate public.ordered_set_aggregate(double precision order by anyelement, text); + +-- comments in every position +drop /* aggregate */ aggregate /* if */ if /* exists */ exists /* first aggregate */ public.first_aggregate(/* first argument */ integer /* first close */) /* before comma */, /* second aggregate */ public.second_aggregate(/* second argument */ text, /* third argument */ numeric /* second close */) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_cast.sql b/crates/squawk_fmt/tests/before/drop_cast.sql new file mode 100644 index 00000000..dee1da08 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_cast.sql @@ -0,0 +1,6 @@ +drop cast (integer as bigint); + +drop cast if exists (extraordinarily_long_schema_name.extraordinarily_long_source_type_name as extraordinarily_long_schema_name.extraordinarily_long_target_type_name) cascade; + +-- comments in every position +drop /* cast */ cast /* if */ if /* exists */ exists /* signature */ (/* source type */ text /* as */ as /* target type */ varchar /* closing parenthesis */) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_collation.sql b/crates/squawk_fmt/tests/before/drop_collation.sql new file mode 100644 index 00000000..ceb12422 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_collation.sql @@ -0,0 +1,6 @@ +drop collation public.english; + +drop collation if exists extraordinarily_long_schema_name.extraordinarily_long_collation_name, another_extraordinarily_long_schema_name.another_extraordinarily_long_collation_name cascade; + +-- comments in every position +drop /* collation */ collation /* if */ if /* exists */ exists /* first collation */ public.english /* before comma */, /* second collation */ public.french /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_conversion.sql b/crates/squawk_fmt/tests/before/drop_conversion.sql new file mode 100644 index 00000000..37c2da7c --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_conversion.sql @@ -0,0 +1,6 @@ +drop conversion public.my_conversion; + +drop conversion if exists extraordinarily_long_schema_name.extraordinarily_long_character_set_conversion_name cascade; + +-- comments in every position +drop /* conversion */ conversion /* if */ if /* exists */ exists /* conversion name */ public.commented_conversion /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_database.sql b/crates/squawk_fmt/tests/before/drop_database.sql new file mode 100644 index 00000000..986d7999 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_database.sql @@ -0,0 +1,8 @@ +drop database application; + +drop database reporting (force); + +drop database if exists extraordinarily_long_database_name_used_to_verify_statement_line_wrapping with (force); + +-- comments in every position +drop /* database */ database /* if */ if /* exists */ exists /* name */ application /* with */ with /* left paren */ ( /* force */ force /* right paren */ ) /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_domain.sql b/crates/squawk_fmt/tests/before/drop_domain.sql new file mode 100644 index 00000000..02d57e47 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_domain.sql @@ -0,0 +1,6 @@ +drop domain public.email_address; + +drop domain if exists extraordinarily_long_schema_name.extraordinarily_long_domain_name, another_extraordinarily_long_schema_name.another_extraordinarily_long_domain_name cascade; + +-- comments in every position +drop /* domain */ domain /* if */ if /* exists */ exists /* first domain */ public.email_address /* before comma */, /* second domain */ public.postal_code /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_event_trigger.sql b/crates/squawk_fmt/tests/before/drop_event_trigger.sql new file mode 100644 index 00000000..d9834653 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_event_trigger.sql @@ -0,0 +1,6 @@ +drop event trigger ddl_audit; + +drop event trigger if exists extraordinarily_long_event_trigger_name_used_to_verify_statement_line_wrapping cascade; + +-- comments in every position +drop /* event */ event /* trigger */ trigger /* if */ if /* exists */ exists /* name */ ddl_audit /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_extension.sql b/crates/squawk_fmt/tests/before/drop_extension.sql new file mode 100644 index 00000000..fde6a4d3 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_extension.sql @@ -0,0 +1,6 @@ +drop extension hstore; + +drop extension if exists extraordinarily_long_extension_name_used_to_verify_statement_line_wrapping, another_extraordinarily_long_extension_name cascade; + +-- comments in every position +drop /* extension */ extension /* if */ if /* exists */ exists /* first extension */ hstore /* before comma */, /* second extension */ pg_trgm /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_foreign_data_wrapper.sql b/crates/squawk_fmt/tests/before/drop_foreign_data_wrapper.sql new file mode 100644 index 00000000..b7871c22 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_foreign_data_wrapper.sql @@ -0,0 +1,6 @@ +drop foreign data wrapper analytics_fdw; + +drop foreign data wrapper if exists extraordinarily_long_foreign_data_wrapper_name_for_analytics, another_extraordinarily_long_foreign_data_wrapper_name cascade; + +-- comments in every position +drop /* foreign */ foreign /* data */ data /* wrapper */ wrapper /* if */ if /* exists */ exists /* first wrapper */ analytics_fdw /* before comma */, /* second wrapper */ reporting_fdw /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_foreign_table.sql b/crates/squawk_fmt/tests/before/drop_foreign_table.sql new file mode 100644 index 00000000..76e507ae --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_foreign_table.sql @@ -0,0 +1,6 @@ +drop foreign table public.events; + +drop foreign table if exists extraordinarily_long_schema_name.extraordinarily_long_foreign_table_name, another_extraordinarily_long_schema_name.another_extraordinarily_long_foreign_table_name cascade; + +-- comments in every position +drop /* foreign */ foreign /* table */ table /* if */ if /* exists */ exists /* first table */ public /* dot */ . events /* before comma */, /* second table */ reporting.archive /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_function.sql b/crates/squawk_fmt/tests/before/drop_function.sql new file mode 100644 index 00000000..c17aeadb --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_function.sql @@ -0,0 +1,6 @@ +drop function public.calculate_total(integer, numeric); + +drop function if exists extraordinarily_long_schema_name.extraordinarily_long_function_name(extraordinarily_long_schema_name.extraordinarily_long_argument_type_name, double precision), public.another_extraordinarily_long_function_name(integer) cascade; + +-- comments in every position +drop /* function */ function /* if */ if /* exists */ exists /* first function */ public /* dot */ . calculate_total /* open */ (/* first argument */ integer /* argument comma */, /* second argument */ numeric /* close */) /* function comma */, /* second function */ reporting.refresh_cache(/* second function argument */ text) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_group.sql b/crates/squawk_fmt/tests/before/drop_group.sql new file mode 100644 index 00000000..eb9148a0 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_group.sql @@ -0,0 +1,6 @@ +drop group analysts; + +drop group if exists extraordinarily_long_group_name_for_application_analytics, another_extraordinarily_long_group_name_for_application_reporting; + +-- comments in every position +drop /* group */ group /* if */ if /* exists */ exists /* first group */ analysts /* before comma */, /* second group */ reporting_users /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_index.sql b/crates/squawk_fmt/tests/before/drop_index.sql new file mode 100644 index 00000000..97d36452 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_index.sql @@ -0,0 +1,6 @@ +drop index public.events_created_at_idx; + +drop index concurrently if exists extraordinarily_long_schema_name.extraordinarily_long_index_name, another_extraordinarily_long_schema_name.another_extraordinarily_long_index_name cascade; + +-- comments in every position +drop /* index */ index /* concurrently */ concurrently /* if */ if /* exists */ exists /* first index */ public /* dot */ . events_idx /* before comma */, /* second index */ reporting.archive_idx /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_language.sql b/crates/squawk_fmt/tests/before/drop_language.sql new file mode 100644 index 00000000..cd56aef8 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_language.sql @@ -0,0 +1,6 @@ +drop language plpgsql; + +drop procedural language if exists extraordinarily_long_procedural_language_name cascade; + +-- comments in every position +drop /* procedural */ procedural /* language */ language /* if */ if /* exists */ exists /* language name */ old_language /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_materialized_view.sql b/crates/squawk_fmt/tests/before/drop_materialized_view.sql new file mode 100644 index 00000000..0ab7b667 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_materialized_view.sql @@ -0,0 +1,6 @@ +drop materialized view public.monthly_sales; + +drop materialized view if exists extraordinarily_long_schema_name.extraordinarily_long_materialized_view_name, another_extraordinarily_long_schema_name.another_extraordinarily_long_materialized_view_name cascade; + +-- comments in every position +drop /* materialized */ materialized /* view */ view /* if */ if /* exists */ exists /* first view */ public /* dot */ . monthly_sales /* before comma */, /* second view */ reporting.archived_sales /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_operator.sql b/crates/squawk_fmt/tests/before/drop_operator.sql new file mode 100644 index 00000000..2bffbb3c --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_operator.sql @@ -0,0 +1,6 @@ +drop operator public.=== (integer, integer); + +drop operator if exists extraordinarily_long_schema_name.=== (extraordinarily_long_schema_name.extraordinarily_long_left_operand_type, extraordinarily_long_schema_name.extraordinarily_long_right_operand_type), public.<> (none, double precision) cascade; + +-- comments in every position +drop /* operator */ operator /* if */ if /* exists */ exists /* first operator */ public /* path dot */ . === /* open */ (/* left type */ integer /* type comma */, /* right type */ integer /* close */) /* signature comma */, /* second operator */ ~ /* second open */ (/* none */ none /* second comma */, /* second type */ text /* second close */) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_operator_class.sql b/crates/squawk_fmt/tests/before/drop_operator_class.sql new file mode 100644 index 00000000..5519743a --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_operator_class.sql @@ -0,0 +1,6 @@ +drop operator class public.text_pattern_ops using btree; + +drop operator class if exists extraordinarily_long_schema_name.extraordinarily_long_operator_class_name using extraordinarily_long_access_method_name cascade; + +-- comments in every position +drop /* operator */ operator /* class */ class /* if */ if /* exists */ exists /* class name */ public /* dot */ . text_pattern_ops /* using */ using /* method */ btree /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_operator_family.sql b/crates/squawk_fmt/tests/before/drop_operator_family.sql new file mode 100644 index 00000000..bd9fe142 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_operator_family.sql @@ -0,0 +1,6 @@ +drop operator family public.integer_ops using btree; + +drop operator family if exists extraordinarily_long_schema_name.extraordinarily_long_operator_family_name using extraordinarily_long_access_method_name restrict; + +-- comments in every position +drop /* operator */ operator /* family */ family /* if */ if /* exists */ exists /* family name */ public /* dot */ . integer_ops /* using */ using /* method */ hash /* behavior */ cascade /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_owned.sql b/crates/squawk_fmt/tests/before/drop_owned.sql new file mode 100644 index 00000000..f2eaa235 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_owned.sql @@ -0,0 +1,6 @@ +drop owned by alice; + +drop owned by extraordinarily_long_database_role_name, another_extraordinarily_long_database_role_name, current_user cascade; + +-- comments in every position +drop /* owned */ owned /* by */ by /* first role */ alice /* first comma */, /* second role */ current_user /* second comma */, /* group keyword */ group /* group name */ managers /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_policy.sql b/crates/squawk_fmt/tests/before/drop_policy.sql new file mode 100644 index 00000000..9b25d381 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_policy.sql @@ -0,0 +1,6 @@ +drop policy account_policy on public.accounts; + +drop policy if exists extraordinarily_long_policy_name_for_customer_account_management on extraordinarily_long_schema_name.extraordinarily_long_customer_accounts_table cascade; + +-- comments in every position +drop /* policy */ policy /* if */ if /* exists */ exists /* policy name */ account_policy /* on */ on /* table schema */ public /* dot */ . accounts /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_procedure.sql b/crates/squawk_fmt/tests/before/drop_procedure.sql new file mode 100644 index 00000000..fc1f2ac2 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_procedure.sql @@ -0,0 +1,6 @@ +drop procedure public.refresh_accounts(integer, text); + +drop procedure if exists extraordinarily_long_schema_name.extraordinarily_long_account_refresh_procedure(extraordinarily_long_schema_name.extraordinarily_long_account_identifier_type, double precision), public.another_extraordinarily_long_procedure_name(integer) cascade; + +-- comments in every position +drop /* procedure */ procedure /* if */ if /* exists */ exists /* first procedure */ public /* dot */ . refresh_accounts /* open */ (/* first argument */ integer /* argument comma */, /* second argument */ text /* close */) /* procedure comma */, /* second procedure */ reporting.rebuild_cache(/* argument */ bigint) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_property_graph.sql b/crates/squawk_fmt/tests/before/drop_property_graph.sql new file mode 100644 index 00000000..38301c99 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_property_graph.sql @@ -0,0 +1,6 @@ +drop property graph social_graph; + +drop property graph if exists extraordinarily_long_schema_name.extraordinarily_long_social_relationship_property_graph cascade; + +-- comments in every position +drop /* property */ property /* graph */ graph /* if */ if /* exists */ exists /* schema */ public /* dot */ . /* graph name */ social_graph /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_role.sql b/crates/squawk_fmt/tests/before/drop_role.sql new file mode 100644 index 00000000..0da99b6d --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_role.sql @@ -0,0 +1,6 @@ +drop role analysts; + +drop role if exists extraordinarily_long_role_name_for_application_analytics_and_business_intelligence, another_extraordinarily_long_role_name_for_application_reporting; + +-- comments in every position +drop /* role */ role /* if */ if /* exists */ exists /* first role */ analysts /* comma */, /* second role */ current_user /* comma */, /* group */ group /* group name */ reporting_users /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_routine.sql b/crates/squawk_fmt/tests/before/drop_routine.sql new file mode 100644 index 00000000..f8ce7160 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_routine.sql @@ -0,0 +1,6 @@ +drop routine public.calculate_total(integer, numeric); + +drop routine if exists extraordinarily_long_schema_name.extraordinarily_long_calculation_routine(extraordinarily_long_schema_name.extraordinarily_long_argument_type_name, double precision), public.another_extraordinarily_long_routine_name(integer) cascade; + +-- comments in every position +drop /* routine */ routine /* if */ if /* exists */ exists /* first routine */ public /* dot */ . calculate_total /* open */ (/* first argument */ integer /* argument comma */, /* second argument */ numeric /* close */) /* routine comma */, /* second routine */ reporting.refresh_cache(/* argument */ text) /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_rule.sql b/crates/squawk_fmt/tests/before/drop_rule.sql new file mode 100644 index 00000000..3c2613a9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_rule.sql @@ -0,0 +1,6 @@ +drop rule archive_order on public.orders; + +drop rule if exists extraordinarily_long_rule_name_for_archiving_completed_customer_orders on extraordinarily_long_schema_name.extraordinarily_long_customer_orders_relation cascade; + +-- comments in every position +drop /* rule */ rule /* if */ if /* exists */ exists /* rule name */ archive_order /* on */ on /* relation schema */ public /* dot */ . orders /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_schema.sql b/crates/squawk_fmt/tests/before/drop_schema.sql new file mode 100644 index 00000000..892ac8c9 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_schema.sql @@ -0,0 +1,6 @@ +drop schema analytics; + +drop schema if exists extraordinarily_long_schema_name_for_customer_reporting_and_business_intelligence, another_extraordinarily_long_schema_name_for_historical_application_analytics cascade; + +-- comments in every position +drop /* schema */ schema /* if */ if /* exists */ exists /* first schema */ analytics /* comma */, /* second schema */ reporting /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_sequence.sql b/crates/squawk_fmt/tests/before/drop_sequence.sql new file mode 100644 index 00000000..a0fbb588 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_sequence.sql @@ -0,0 +1,6 @@ +drop sequence public.order_number_seq; + +drop sequence if exists extraordinarily_long_schema_name.extraordinarily_long_sequence_name_for_customer_order_numbers, another_extraordinarily_long_schema_name.another_extraordinarily_long_sequence_name cascade; + +-- comments in every position +drop /* sequence */ sequence /* if */ if /* exists */ exists /* first schema */ public /* first dot */ . order_number_seq /* comma */, /* second schema */ accounting /* second dot */ . invoice_number_seq /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_server.sql b/crates/squawk_fmt/tests/before/drop_server.sql new file mode 100644 index 00000000..fd27edc2 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_server.sql @@ -0,0 +1,6 @@ +drop server reporting_server; + +drop server if exists extraordinarily_long_foreign_server_name_for_customer_reporting_database, another_extraordinarily_long_foreign_server_name_for_historical_analytics cascade; + +-- comments in every position +drop /* server */ server /* if */ if /* exists */ exists /* first server */ reporting_server /* comma */, /* second server */ archive_server /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_statistics.sql b/crates/squawk_fmt/tests/before/drop_statistics.sql new file mode 100644 index 00000000..64e1b8ca --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_statistics.sql @@ -0,0 +1,6 @@ +drop statistics public.user_activity_stats; + +drop statistics if exists extraordinarily_long_schema_name.extraordinarily_long_statistics_name_for_customer_activity, another_extraordinarily_long_schema_name.another_extraordinarily_long_statistics_name cascade; + +-- comments in every position +drop /* statistics */ statistics /* if */ if /* exists */ exists /* first statistics */ public /* dot */ . user_stats /* before comma */, /* second statistics */ reporting.activity_stats /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_table.sql b/crates/squawk_fmt/tests/before/drop_table.sql new file mode 100644 index 00000000..9b807013 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_table.sql @@ -0,0 +1,6 @@ +drop table public.accounts; + +drop table if exists extraordinarily_long_schema_name.extraordinarily_long_table_name_for_customer_accounts, another_extraordinarily_long_schema_name.another_extraordinarily_long_table_name cascade; + +-- comments in every position +drop /* table */ table /* if */ if /* exists */ exists /* first table */ public /* dot */ . accounts /* before comma */, /* second table */ archive.old_accounts /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_tablespace.sql b/crates/squawk_fmt/tests/before/drop_tablespace.sql new file mode 100644 index 00000000..7397e9c4 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_tablespace.sql @@ -0,0 +1,6 @@ +drop tablespace fast_storage; + +drop tablespace if exists extraordinarily_long_tablespace_name_for_historical_customer_reporting_data; + +-- comments in every position +drop /* tablespace */ tablespace /* if */ if /* exists */ exists /* tablespace */ archive_storage /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_text_search_config.sql b/crates/squawk_fmt/tests/before/drop_text_search_config.sql new file mode 100644 index 00000000..0daba697 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_text_search_config.sql @@ -0,0 +1,6 @@ +drop text search configuration public.english; + +drop text search configuration if exists extraordinarily_long_schema_name.extraordinarily_long_text_search_configuration_for_customer_documents cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* configuration */ configuration /* if */ if /* exists */ exists /* configuration */ public /* dot */ . english /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_text_search_dict.sql b/crates/squawk_fmt/tests/before/drop_text_search_dict.sql new file mode 100644 index 00000000..68d993b8 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_text_search_dict.sql @@ -0,0 +1,6 @@ +drop text search dictionary public.english_stem; + +drop text search dictionary if exists extraordinarily_long_schema_name.extraordinarily_long_text_search_dictionary_for_customer_documents cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* dictionary */ dictionary /* if */ if /* exists */ exists /* dictionary */ public /* dot */ . english_stem /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_text_search_parser.sql b/crates/squawk_fmt/tests/before/drop_text_search_parser.sql new file mode 100644 index 00000000..c79e3737 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_text_search_parser.sql @@ -0,0 +1,6 @@ +drop text search parser public.default_parser; + +drop text search parser if exists extraordinarily_long_schema_name.extraordinarily_long_text_search_parser_for_customer_documents cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* parser */ parser /* if */ if /* exists */ exists /* parser name */ public /* dot */ . default_parser /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_text_search_template.sql b/crates/squawk_fmt/tests/before/drop_text_search_template.sql new file mode 100644 index 00000000..a91f08e3 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_text_search_template.sql @@ -0,0 +1,6 @@ +drop text search template public.simple_template; + +drop text search template if exists extraordinarily_long_schema_name.extraordinarily_long_text_search_template_for_customer_documents cascade; + +-- comments in every position +drop /* text */ text /* search */ search /* template */ template /* if */ if /* exists */ exists /* template name */ public /* dot */ . simple_template /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_transform.sql b/crates/squawk_fmt/tests/before/drop_transform.sql new file mode 100644 index 00000000..f6152a88 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_transform.sql @@ -0,0 +1,6 @@ +drop transform for public.customer_document language plpgsql; + +drop transform if exists for extraordinarily_long_schema_name.extraordinarily_long_customer_document_type_name language extraordinarily_long_procedural_language_name cascade; + +-- comments in every position +drop /* transform */ transform /* if */ if /* exists */ exists /* for */ for /* type */ public /* dot */ . customer_document /* language */ language /* language name */ plpgsql /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_trigger.sql b/crates/squawk_fmt/tests/before/drop_trigger.sql new file mode 100644 index 00000000..122c3ad5 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_trigger.sql @@ -0,0 +1,6 @@ +drop trigger audit_trigger on public.accounts; + +drop trigger if exists extraordinarily_long_audit_trigger_name_for_customer_account_changes on extraordinarily_long_schema_name.extraordinarily_long_customer_accounts_table cascade; + +-- comments in every position +drop /* trigger */ trigger /* if */ if /* exists */ exists /* name */ audit_trigger /* on */ on /* relation */ public /* dot */ . accounts /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_type.sql b/crates/squawk_fmt/tests/before/drop_type.sql new file mode 100644 index 00000000..7f40bb41 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_type.sql @@ -0,0 +1,6 @@ +drop type public.customer_kind; + +drop type if exists extraordinarily_long_schema_name.extraordinarily_long_customer_classification_type, extraordinarily_long_schema_name.another_extraordinarily_long_customer_status_type cascade; + +-- comments in every position +drop /* type */ type /* if */ if /* exists */ exists /* first */ public /* dot */ . customer_kind /* comma */, /* second */ reporting.event_kind /* behavior */ restrict /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_user.sql b/crates/squawk_fmt/tests/before/drop_user.sql new file mode 100644 index 00000000..db8cd6a0 --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_user.sql @@ -0,0 +1,6 @@ +drop user alice; + +drop user if exists extraordinarily_long_application_reporting_user_name, extraordinarily_long_customer_support_user_name, current_user; + +-- comments in every position +drop /* user */ user /* if */ if /* exists */ exists /* first */ alice /* comma */, /* second */ current_user /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_user_mapping.sql b/crates/squawk_fmt/tests/before/drop_user_mapping.sql new file mode 100644 index 00000000..f141c05e --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_user_mapping.sql @@ -0,0 +1,6 @@ +drop user mapping for app_user server app_server; + +drop user mapping if exists for extraordinarily_long_application_reporting_user_name server extraordinarily_long_foreign_data_server_name; + +-- comments in every position +drop /* user */ user /* mapping */ mapping /* if */ if /* exists */ exists /* for */ for /* role */ current_user /* server */ server /* server name */ remote_server /* end */; diff --git a/crates/squawk_fmt/tests/before/drop_view.sql b/crates/squawk_fmt/tests/before/drop_view.sql new file mode 100644 index 00000000..1a92814a --- /dev/null +++ b/crates/squawk_fmt/tests/before/drop_view.sql @@ -0,0 +1,6 @@ +drop view public.active_accounts; + +drop view if exists extraordinarily_long_schema_name.extraordinarily_long_view_name_for_customer_accounts, another_extraordinarily_long_schema_name.another_extraordinarily_long_archived_accounts_view cascade; + +-- comments in every position +drop /* view */ view /* if */ if /* exists */ exists /* first view */ public /* dot */ . active_accounts /* before comma */, /* second view */ reporting.archived_accounts /* behavior */ restrict /* end */; diff --git a/crates/squawk_ide/src/binder.rs b/crates/squawk_ide/src/binder.rs index 5563eb39..624a31bf 100644 --- a/crates/squawk_ide/src/binder.rs +++ b/crates/squawk_ide/src/binder.rs @@ -2079,7 +2079,7 @@ pub(crate) fn extract_string_literal(literal: &ast::Literal) -> Option { fn extract_param_signature(param_list: Option) -> Option> { let param_list = param_list?; let mut params = vec![]; - for param in param_list.params() { + for param in param_list.all_params() { if let Some(ty) = param.ty() && let ast::Type::PathType(path_type) = ty && let Some(path) = path_type.path_ref() diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index a998a74f..6779f481 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -1032,7 +1032,7 @@ fn resolve_positional_param( let Some(param_list) = has_param_list.param_list() else { continue; }; - if let Some(param) = param_list.params().nth(index) { + if let Some(param) = param_list.all_params().nth(index) { let range = param .name() .map(|name| name.syntax().text_range()) @@ -2864,7 +2864,7 @@ fn resolve_enclosing_function_param( let Some(param_list) = has_param_list.param_list() else { continue; }; - for param in param_list.params() { + for param in param_list.all_params() { if let Some(name) = param.name() && Name::from_node(&name) == param_name { @@ -5181,7 +5181,7 @@ fn resolve_symbol_info_from_parts( fn param_signature(node: &ast::HasParamList) -> Option> { let mut params = vec![]; - for param in node.param_list()?.params() { + for param in node.param_list()?.all_params() { if let Some(ast::Type::PathType(path_type)) = param.ty() && let Some(name_ref) = path_type.path_ref().and_then(|x| x.segment()) { @@ -5740,7 +5740,7 @@ fn find_param_in_func_def( } })?; - for param in param_list.params() { + for param in param_list.all_params() { if let Some(name) = param.name() && Name::from_node(&name) == *param_name { diff --git a/crates/squawk_parser/src/generated/syntax_kind.rs b/crates/squawk_parser/src/generated/syntax_kind.rs index 845fca62..b6aa88df 100644 --- a/crates/squawk_parser/src/generated/syntax_kind.rs +++ b/crates/squawk_parser/src/generated/syntax_kind.rs @@ -596,6 +596,7 @@ pub enum SyntaxKind { AFTER_VALUE, AGGREGATE, AGGREGATE_NAME, + AGGREGATE_ORDER_BY, AGGREGATE_RENAME_TO, ALL, ALL_FN, @@ -983,6 +984,7 @@ pub enum SyntaxKind { EVENT_TRIGGER_REF, EVENT_TRIGGER_RENAME_TO, EVENT_TRIGGER_WHEN, + EVENT_TRIGGER_WHEN_AND, EVENT_TRIGGER_WHEN_CLAUSE, EXCEPT, EXCEPT_TABLES, diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index b73fb4b0..bd4e8238 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -12005,8 +12005,11 @@ fn opt_event_trigger_when_clause(p: &mut Parser<'_>) { let m = p.start(); p.bump(WHEN_KW); event_trigger_when(p); - while !p.at(EOF) && p.eat(AND_KW) { + while !p.at(EOF) && p.at(AND_KW) { + let m = p.start(); + p.bump(AND_KW); event_trigger_when(p); + m.complete(p, EVENT_TRIGGER_WHEN_AND); } m.complete(p, EVENT_TRIGGER_WHEN_CLAUSE); } @@ -14962,49 +14965,32 @@ fn security_label_provider(p: &mut Parser<'_>) { fn agg_args(p: &mut Parser<'_>) { match p.current() { - STAR => { - p.bump(STAR); - } - // ORDER BY [ argmode ] [ argname ] argtype [ , ... ] - ORDER_KW => { - p.bump(ORDER_KW); - p.expect(BY_KW); - // TODO: generalize - param(p, ParamKind::All); - while !p.at(EOF) { - if p.eat(COMMA) { - param(p, ParamKind::All); - } else { - break; - } - } - } + STAR => p.bump(STAR), + ORDER_KW => aggregate_order_by(p), _ => { param(p, ParamKind::All); - while !p.at(EOF) { - if p.eat(COMMA) { - param(p, ParamKind::All); - } else { - break; - } - } - // ORDER BY [ argmode ] [ argname ] argtype [ , ... ] - if p.eat(ORDER_KW) { - p.expect(BY_KW); - // TODO: generalize + while p.eat(COMMA) { param(p, ParamKind::All); - while !p.at(EOF) { - if p.eat(COMMA) { - param(p, ParamKind::All); - } else { - break; - } - } + } + if p.at(ORDER_KW) { + aggregate_order_by(p); } } } } +// ORDER BY [ argmode ] [ argname ] argtype [ , ... ] +fn aggregate_order_by(p: &mut Parser<'_>) { + let m = p.start(); + p.bump(ORDER_KW); + p.expect(BY_KW); + param(p, ParamKind::All); + while p.eat(COMMA) { + param(p, ParamKind::All); + } + m.complete(p, AGGREGATE_ORDER_BY); +} + fn aggregate_arg_list(p: &mut Parser<'_>) { let m = p.start(); p.expect(L_PAREN); diff --git a/crates/squawk_parser/tests/snapshots/tests__alter_aggregate_ok.snap b/crates/squawk_parser/tests/snapshots/tests__alter_aggregate_ok.snap index b9b62619..8e736373 100644 --- a/crates/squawk_parser/tests/snapshots/tests__alter_aggregate_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__alter_aggregate_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/alter_aggregate.sql --- SOURCE_FILE @@ -127,15 +128,16 @@ SOURCE_FILE PATH_SEGMENT_REF IDENT "x" WHITESPACE " " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE " " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - IDENT "y" + AGGREGATE_ORDER_BY + ORDER_KW "order" + WHITESPACE " " + BY_KW "by" + WHITESPACE " " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + IDENT "y" R_PAREN ")" WHITESPACE " \n " AGGREGATE_RENAME_TO diff --git a/crates/squawk_parser/tests/snapshots/tests__alter_extension_ok.snap b/crates/squawk_parser/tests/snapshots/tests__alter_extension_ok.snap index 336a19d4..8d1ae8d1 100644 --- a/crates/squawk_parser/tests/snapshots/tests__alter_extension_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__alter_extension_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/alter_extension.sql --- SOURCE_FILE @@ -150,22 +151,23 @@ SOURCE_FILE PATH_SEGMENT_REF TEXT_KW "text" WHITESPACE " " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE " " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - IDENT "b" - COMMA "," - WHITESPACE " " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - IDENT "c" + AGGREGATE_ORDER_BY + ORDER_KW "order" + WHITESPACE " " + BY_KW "by" + WHITESPACE " " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + IDENT "b" + COMMA "," + WHITESPACE " " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + IDENT "c" WHITESPACE " " R_PAREN ")" SEMICOLON ";" diff --git a/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap b/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap index ab9a64fe..6a55ae31 100644 --- a/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/comment.sql --- SOURCE_FILE @@ -149,13 +150,14 @@ SOURCE_FILE PATH_SEGMENT_REF TEXT_KW "text" WHITESPACE " " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE " " - PARAM - TIMESTAMP_TYPE - TIMESTAMP_KW "timestamp" + AGGREGATE_ORDER_BY + ORDER_KW "order" + WHITESPACE " " + BY_KW "by" + WHITESPACE " " + PARAM + TIMESTAMP_TYPE + TIMESTAMP_KW "timestamp" R_PAREN ")" WHITESPACE " " IS_KW "is" diff --git a/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap index 2c314cdc..d778c9a4 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/create_aggregate.sql --- SOURCE_FILE @@ -756,25 +757,26 @@ SOURCE_FILE TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " - ORDER_KW "ORDER" - WHITESPACE " " - BY_KW "BY" - WHITESPACE " " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - IDENT "a" - COMMA "," - WHITESPACE "\n " - PARAM - PARAM_NAME - IDENT "result" + AGGREGATE_ORDER_BY + ORDER_KW "ORDER" WHITESPACE " " - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - NUMERIC_KW "numeric" + BY_KW "BY" + WHITESPACE " " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + IDENT "a" + COMMA "," + WHITESPACE "\n " + PARAM + PARAM_NAME + IDENT "result" + WHITESPACE " " + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + NUMERIC_KW "numeric" WHITESPACE "\n" R_PAREN ")" WHITESPACE " " @@ -1033,15 +1035,16 @@ SOURCE_FILE PATH_SEGMENT_REF IDENT "float8" WHITESPACE " " - ORDER_KW "ORDER" - WHITESPACE " " - BY_KW "BY" - WHITESPACE " " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - IDENT "anyelement" + AGGREGATE_ORDER_BY + ORDER_KW "ORDER" + WHITESPACE " " + BY_KW "BY" + WHITESPACE " " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + IDENT "anyelement" R_PAREN ")" WHITESPACE "\n" ATTRIBUTE_LIST diff --git a/crates/squawk_parser/tests/snapshots/tests__create_event_trigger_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_event_trigger_ok.snap index 3ad2bb9d..12b75096 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_event_trigger_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_event_trigger_ok.snap @@ -71,18 +71,19 @@ SOURCE_FILE STRING "'buzz'" R_PAREN ")" WHITESPACE "\n " - AND_KW "and" - WHITESPACE " " - EVENT_TRIGGER_WHEN - EVENT_TRIGGER_FILTER_VARIABLE - REAL_KW "real" - WHITESPACE " " - IN_KW "in" + EVENT_TRIGGER_WHEN_AND + AND_KW "and" WHITESPACE " " - L_PAREN "(" - LITERAL - STRING "'a'" - R_PAREN ")" + EVENT_TRIGGER_WHEN + EVENT_TRIGGER_FILTER_VARIABLE + REAL_KW "real" + WHITESPACE " " + IN_KW "in" + WHITESPACE " " + L_PAREN "(" + LITERAL + STRING "'a'" + R_PAREN ")" WHITESPACE "\n " EXECUTE_KW "execute" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap b/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap index 7a96c03e..94ededef 100644 --- a/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/drop_aggregate.sql --- SOURCE_FILE @@ -146,36 +147,37 @@ SOURCE_FILE PATH_SEGMENT_REF NUMERIC_KW "numeric" WHITESPACE "\n " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE "\n " - PARAM - PARAM_IN - IN_KW "in" - WHITESPACE " " - PARAM_NAME - IDENT "a" - WHITESPACE " " - TIMESTAMP_TYPE - TIMESTAMP_KW "timestamp" - COMMA "," - WHITESPACE "\n " - PARAM - PARAM_NAME - IDENT "b" + AGGREGATE_ORDER_BY + ORDER_KW "order" WHITESPACE " " - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - NUMERIC_KW "numeric" - COMMA "," - WHITESPACE "\n " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - TEXT_KW "text" + BY_KW "by" + WHITESPACE "\n " + PARAM + PARAM_IN + IN_KW "in" + WHITESPACE " " + PARAM_NAME + IDENT "a" + WHITESPACE " " + TIMESTAMP_TYPE + TIMESTAMP_KW "timestamp" + COMMA "," + WHITESPACE "\n " + PARAM + PARAM_NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + NUMERIC_KW "numeric" + COMMA "," + WHITESPACE "\n " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + TEXT_KW "text" WHITESPACE "\n" R_PAREN ")" WHITESPACE " " @@ -199,36 +201,37 @@ SOURCE_FILE PARAM_LIST L_PAREN "(" WHITESPACE "\n " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE "\n " - PARAM - PARAM_IN - IN_KW "in" - WHITESPACE " " - PARAM_NAME - IDENT "a" - WHITESPACE " " - TIMESTAMP_TYPE - TIMESTAMP_KW "timestamp" - COMMA "," - WHITESPACE "\n " - PARAM - PARAM_NAME - IDENT "b" + AGGREGATE_ORDER_BY + ORDER_KW "order" WHITESPACE " " - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - NUMERIC_KW "numeric" - COMMA "," - WHITESPACE "\n " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - TEXT_KW "text" + BY_KW "by" + WHITESPACE "\n " + PARAM + PARAM_IN + IN_KW "in" + WHITESPACE " " + PARAM_NAME + IDENT "a" + WHITESPACE " " + TIMESTAMP_TYPE + TIMESTAMP_KW "timestamp" + COMMA "," + WHITESPACE "\n " + PARAM + PARAM_NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + NUMERIC_KW "numeric" + COMMA "," + WHITESPACE "\n " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + TEXT_KW "text" WHITESPACE "\n" R_PAREN ")" SEMICOLON ";" diff --git a/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap b/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap index fa1ae946..efbc216a 100644 --- a/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap @@ -1,5 +1,6 @@ --- source: crates/squawk_parser/tests/tests.rs +assertion_line: 28 input_file: crates/squawk_parser/tests/data/ok/security_label.sql --- SOURCE_FILE @@ -218,36 +219,37 @@ SOURCE_FILE PATH_SEGMENT_REF NUMERIC_KW "numeric" WHITESPACE "\n " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE "\n " - PARAM - PARAM_IN - IN_KW "in" - WHITESPACE " " - PARAM_NAME - IDENT "a" - WHITESPACE " " - TIMESTAMP_TYPE - TIMESTAMP_KW "timestamp" - COMMA "," - WHITESPACE "\n " - PARAM - PARAM_NAME - IDENT "b" + AGGREGATE_ORDER_BY + ORDER_KW "order" WHITESPACE " " - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - NUMERIC_KW "numeric" - COMMA "," - WHITESPACE "\n " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - TEXT_KW "text" + BY_KW "by" + WHITESPACE "\n " + PARAM + PARAM_IN + IN_KW "in" + WHITESPACE " " + PARAM_NAME + IDENT "a" + WHITESPACE " " + TIMESTAMP_TYPE + TIMESTAMP_KW "timestamp" + COMMA "," + WHITESPACE "\n " + PARAM + PARAM_NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + NUMERIC_KW "numeric" + COMMA "," + WHITESPACE "\n " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + TEXT_KW "text" WHITESPACE "\n" R_PAREN ")" WHITESPACE " " @@ -278,36 +280,37 @@ SOURCE_FILE PARAM_LIST L_PAREN "(" WHITESPACE "\n " - ORDER_KW "order" - WHITESPACE " " - BY_KW "by" - WHITESPACE "\n " - PARAM - PARAM_IN - IN_KW "in" - WHITESPACE " " - PARAM_NAME - IDENT "a" - WHITESPACE " " - TIMESTAMP_TYPE - TIMESTAMP_KW "timestamp" - COMMA "," - WHITESPACE "\n " - PARAM - PARAM_NAME - IDENT "b" + AGGREGATE_ORDER_BY + ORDER_KW "order" WHITESPACE " " - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - NUMERIC_KW "numeric" - COMMA "," - WHITESPACE "\n " - PARAM - PATH_TYPE - PATH_REF - PATH_SEGMENT_REF - TEXT_KW "text" + BY_KW "by" + WHITESPACE "\n " + PARAM + PARAM_IN + IN_KW "in" + WHITESPACE " " + PARAM_NAME + IDENT "a" + WHITESPACE " " + TIMESTAMP_TYPE + TIMESTAMP_KW "timestamp" + COMMA "," + WHITESPACE "\n " + PARAM + PARAM_NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + NUMERIC_KW "numeric" + COMMA "," + WHITESPACE "\n " + PARAM + PATH_TYPE + PATH_REF + PATH_SEGMENT_REF + TEXT_KW "text" WHITESPACE "\n" R_PAREN ")" WHITESPACE " " diff --git a/crates/squawk_syntax/src/ast.rs b/crates/squawk_syntax/src/ast.rs index c4068066..48a9c6ef 100644 --- a/crates/squawk_syntax/src/ast.rs +++ b/crates/squawk_syntax/src/ast.rs @@ -41,7 +41,7 @@ pub use self::{ generated::tokens::*, node_ext::{BinOp, LitKind, PostfixOp, PrefixOp}, nodes::*, - traits::{HasCreateTable, HasWithClause, NameLike}, + traits::{HasCreateTable, HasPathRef, HasWithClause, NameLike}, }; /// The main trait to go from untyped `SyntaxNode` to a typed ast. The diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index 96d92770..6e9d3cc7 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -518,6 +518,25 @@ impl AggregateName { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AggregateOrderBy { + pub(crate) syntax: SyntaxNode, +} +impl AggregateOrderBy { + #[inline] + pub fn params(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn by_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::BY_KW) + } + #[inline] + pub fn order_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ORDER_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AggregateRenameTo { pub(crate) syntax: SyntaxNode, @@ -2841,6 +2860,10 @@ pub struct AttributeValue { pub(crate) syntax: SyntaxNode, } impl AttributeValue { + #[inline] + pub fn custom_op(&self) -> Option { + support::child(&self.syntax) + } #[inline] pub fn literal(&self) -> Option { support::child(&self.syntax) @@ -10374,13 +10397,32 @@ impl EventTriggerWhen { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EventTriggerWhenAnd { + pub(crate) syntax: SyntaxNode, +} +impl EventTriggerWhenAnd { + #[inline] + pub fn event_trigger_when(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn and_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::AND_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EventTriggerWhenClause { pub(crate) syntax: SyntaxNode, } impl EventTriggerWhenClause { #[inline] - pub fn event_trigger_whens(&self) -> AstChildren { + pub fn event_trigger_when(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn event_trigger_when_ands(&self) -> AstChildren { support::children(&self.syntax) } #[inline] @@ -18468,6 +18510,10 @@ pub struct ParamList { pub(crate) syntax: SyntaxNode, } impl ParamList { + #[inline] + pub fn aggregate_order_by(&self) -> Option { + support::child(&self.syntax) + } #[inline] pub fn params(&self) -> AstChildren { support::children(&self.syntax) @@ -18484,14 +18530,6 @@ impl ParamList { pub fn star_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::STAR) } - #[inline] - pub fn by_token(&self) -> Option { - support::token(&self.syntax, SyntaxKind::BY_KW) - } - #[inline] - pub fn order_token(&self) -> Option { - support::token(&self.syntax, SyntaxKind::ORDER_KW) - } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -30323,6 +30361,24 @@ impl AstNode for AggregateName { &self.syntax } } +impl AstNode for AggregateOrderBy { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::AGGREGATE_ORDER_BY + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for AggregateRenameTo { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -37289,6 +37345,24 @@ impl AstNode for EventTriggerWhen { &self.syntax } } +impl AstNode for EventTriggerWhenAnd { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EVENT_TRIGGER_WHEN_AND + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for EventTriggerWhenClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { diff --git a/crates/squawk_syntax/src/ast/node_ext.rs b/crates/squawk_syntax/src/ast/node_ext.rs index bd259d47..806691c2 100644 --- a/crates/squawk_syntax/src/ast/node_ext.rs +++ b/crates/squawk_syntax/src/ast/node_ext.rs @@ -43,6 +43,14 @@ use crate::{SyntaxKind, SyntaxNode, SyntaxToken, TokenText}; use super::support; +impl ast::CustomOp { + pub fn tokens(&self) -> impl Iterator + '_ { + self.syntax() + .children_with_tokens() + .filter_map(|element| element.into_token()) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum LitKind { BitString(SyntaxToken), @@ -1228,6 +1236,16 @@ impl ast::SelectVariant { } } +impl ast::ParamList { + pub fn all_params(&self) -> impl Iterator { + self.params().chain( + self.aggregate_order_by() + .into_iter() + .flat_map(|order_by| order_by.params()), + ) + } +} + impl ast::HasParamList { #[inline] pub fn param_list(&self) -> Option { @@ -1270,6 +1288,34 @@ where } } +impl ast::HasPathRef for ast::Aggregate {} +impl ast::HasPathRef for ast::CollationRef {} +impl ast::HasPathRef for ast::ConfigParameterRef {} +impl ast::HasPathRef for ast::ConstraintNameRef {} +impl ast::HasPathRef for ast::ConversionRef {} +impl ast::HasPathRef for ast::DomainRef {} +impl ast::HasPathRef for ast::FunctionNameRef {} +impl ast::HasPathRef for ast::IndexRef {} +impl ast::HasPathRef for ast::Op {} +impl ast::HasPathRef for ast::OpClassRef {} +impl ast::HasPathRef for ast::OpFamilyRef {} +impl ast::HasPathRef for ast::PathType {} +impl ast::HasPathRef for ast::PercentType {} +impl ast::HasPathRef for ast::ProcedureNameRef {} +impl ast::HasPathRef for ast::PropertyGraphRef {} +impl ast::HasPathRef for ast::QualifiedColumnNameRef {} +impl ast::HasPathRef for ast::RelationNameRef {} +impl ast::HasPathRef for ast::RoutineNameRef {} +impl ast::HasPathRef for ast::SequenceRef {} +impl ast::HasPathRef for ast::StatisticsRef {} +impl ast::HasPathRef for ast::TableNameRef {} +impl ast::HasPathRef for ast::TextSearchConfigurationRef {} +impl ast::HasPathRef for ast::TextSearchDictionaryRef {} +impl ast::HasPathRef for ast::TextSearchParserRef {} +impl ast::HasPathRef for ast::TextSearchTemplateRef {} +impl ast::HasPathRef for ast::TypeNameRef {} +impl ast::HasPathRef for ast::ViewRef {} + impl ast::HasWithClause for ast::Select {} impl ast::HasWithClause for ast::SelectInto {} impl ast::HasWithClause for ast::Insert {} diff --git a/crates/squawk_syntax/src/ast/traits.rs b/crates/squawk_syntax/src/ast/traits.rs index 0cd5e0d4..4ecc2820 100644 --- a/crates/squawk_syntax/src/ast/traits.rs +++ b/crates/squawk_syntax/src/ast/traits.rs @@ -8,6 +8,13 @@ pub trait NameLike: AstNode { fn is_quoted(&self) -> bool; } +pub trait HasPathRef: AstNode { + #[inline] + fn path_ref(&self) -> Option { + support::child(self.syntax()) + } +} + pub trait HasCreateTable: AstNode { #[inline] fn table_name(&self) -> Option { diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 4b345993..4f38867c 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -69,7 +69,10 @@ ParamNameRef = ParamList = '(' '*' ')' -| '(' (Param (',' Param)*)? ('order' 'by' (Param (',' Param)*))? ')' +| '(' (Param (',' Param)*)? AggregateOrderBy? ')' + +AggregateOrderBy = + 'order' 'by' (Param (',' Param)*) ArgList = '(' '*' ')' @@ -2706,6 +2709,7 @@ AttributeValue = | 'none' | 'operator' '(' Op ')' | Op +| CustomOp | Type AttributeList = @@ -4371,7 +4375,10 @@ CreateEventTrigger = CallExpr ';'? EventTriggerWhenClause = - 'when' (EventTriggerWhen ('and' EventTriggerWhen)*) + 'when' EventTriggerWhen EventTriggerWhenAnd* + +EventTriggerWhenAnd = + 'and' EventTriggerWhen EventTriggerWhen = var:EventTriggerFilterVariable 'in' '(' (Literal (',' Literal)*) ')' diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap index a0194433..6b922fcb 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap @@ -133,39 +133,40 @@ SOURCE_FILE@0..389 PATH_SEGMENT_REF@222..229 NUMERIC_KW@222..229 "numeric" WHITESPACE@229..232 "\n " - ORDER_KW@232..237 "order" - WHITESPACE@237..238 " " - BY_KW@238..240 "by" - WHITESPACE@240..245 "\n " - PARAM@245..259 - PARAM_IN@245..247 - IN_KW@245..247 "in" - WHITESPACE@247..248 " " - PARAM_NAME@248..249 - IDENT@248..249 "a" - WHITESPACE@249..250 " " - TIMESTAMP_TYPE@250..259 - TIMESTAMP_KW@250..259 "timestamp" - COMMA@259..260 "," - WHITESPACE@260..265 "\n " - PARAM@265..278 - PARAM_OUT@265..268 - OUT_KW@265..268 "out" - WHITESPACE@268..269 " " - PARAM_NAME@269..270 - IDENT@269..270 "b" - WHITESPACE@270..271 " " - PATH_TYPE@271..278 - PATH_REF@271..278 - PATH_SEGMENT_REF@271..278 - NUMERIC_KW@271..278 "numeric" - COMMA@278..279 "," - WHITESPACE@279..284 "\n " - PARAM@284..288 - PATH_TYPE@284..288 - PATH_REF@284..288 - PATH_SEGMENT_REF@284..288 - TEXT_KW@284..288 "text" + AGGREGATE_ORDER_BY@232..288 + ORDER_KW@232..237 "order" + WHITESPACE@237..238 " " + BY_KW@238..240 "by" + WHITESPACE@240..245 "\n " + PARAM@245..259 + PARAM_IN@245..247 + IN_KW@245..247 "in" + WHITESPACE@247..248 " " + PARAM_NAME@248..249 + IDENT@248..249 "a" + WHITESPACE@249..250 " " + TIMESTAMP_TYPE@250..259 + TIMESTAMP_KW@250..259 "timestamp" + COMMA@259..260 "," + WHITESPACE@260..265 "\n " + PARAM@265..278 + PARAM_OUT@265..268 + OUT_KW@265..268 "out" + WHITESPACE@268..269 " " + PARAM_NAME@269..270 + IDENT@269..270 "b" + WHITESPACE@270..271 " " + PATH_TYPE@271..278 + PATH_REF@271..278 + PATH_SEGMENT_REF@271..278 + NUMERIC_KW@271..278 "numeric" + COMMA@278..279 "," + WHITESPACE@279..284 "\n " + PARAM@284..288 + PATH_TYPE@284..288 + PATH_REF@284..288 + PATH_SEGMENT_REF@284..288 + TEXT_KW@284..288 "text" WHITESPACE@288..289 "\n" R_PAREN@289..290 ")" WHITESPACE@290..291 " " @@ -189,39 +190,40 @@ SOURCE_FILE@0..389 PARAM_LIST@324..386 L_PAREN@324..325 "(" WHITESPACE@325..328 "\n " - ORDER_KW@328..333 "order" - WHITESPACE@333..334 " " - BY_KW@334..336 "by" - WHITESPACE@336..341 "\n " - PARAM@341..355 - PARAM_IN@341..343 - IN_KW@341..343 "in" - WHITESPACE@343..344 " " - PARAM_NAME@344..345 - IDENT@344..345 "a" - WHITESPACE@345..346 " " - TIMESTAMP_TYPE@346..355 - TIMESTAMP_KW@346..355 "timestamp" - COMMA@355..356 "," - WHITESPACE@356..361 "\n " - PARAM@361..374 - PARAM_OUT@361..364 - OUT_KW@361..364 "out" - WHITESPACE@364..365 " " - PARAM_NAME@365..366 - IDENT@365..366 "b" - WHITESPACE@366..367 " " - PATH_TYPE@367..374 - PATH_REF@367..374 - PATH_SEGMENT_REF@367..374 - NUMERIC_KW@367..374 "numeric" - COMMA@374..375 "," - WHITESPACE@375..380 "\n " - PARAM@380..384 - PATH_TYPE@380..384 - PATH_REF@380..384 - PATH_SEGMENT_REF@380..384 - TEXT_KW@380..384 "text" + AGGREGATE_ORDER_BY@328..384 + ORDER_KW@328..333 "order" + WHITESPACE@333..334 " " + BY_KW@334..336 "by" + WHITESPACE@336..341 "\n " + PARAM@341..355 + PARAM_IN@341..343 + IN_KW@341..343 "in" + WHITESPACE@343..344 " " + PARAM_NAME@344..345 + IDENT@344..345 "a" + WHITESPACE@345..346 " " + TIMESTAMP_TYPE@346..355 + TIMESTAMP_KW@346..355 "timestamp" + COMMA@355..356 "," + WHITESPACE@356..361 "\n " + PARAM@361..374 + PARAM_OUT@361..364 + OUT_KW@361..364 "out" + WHITESPACE@364..365 " " + PARAM_NAME@365..366 + IDENT@365..366 "b" + WHITESPACE@366..367 " " + PATH_TYPE@367..374 + PATH_REF@367..374 + PATH_SEGMENT_REF@367..374 + NUMERIC_KW@367..374 "numeric" + COMMA@374..375 "," + WHITESPACE@375..380 "\n " + PARAM@380..384 + PATH_TYPE@380..384 + PATH_REF@380..384 + PATH_SEGMENT_REF@380..384 + TEXT_KW@380..384 "text" WHITESPACE@384..385 "\n" R_PAREN@385..386 ")" SEMICOLON@386..387 ";" diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index ad26adcc..2d9b94c5 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -761,7 +761,7 @@ fn validate_custom_op(op: ast::CustomOp, acc: &mut Vec) { fn validate_aggregate_params(aggregate_params: Option, acc: &mut Vec) { if let Some(params) = aggregate_params { - for p in params.params() { + for p in params.all_params() { if let Some(mode) = p.mode() { match mode { ast::ParamMode::ParamOut(param_out) => acc.push(SyntaxError::new(