From ccfb9c0096414d3b591880d860635dcac863d97f Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Tue, 1 Sep 2026 19:16:57 -0400 Subject: [PATCH] fmt: adjust values rows & keyword quoting --- crates/squawk_fmt/src/fmt.rs | 109 ++++++++++++---- .../squawk_fmt/tests/after/alter_policy.snap | 6 +- .../squawk_fmt/tests/after/create_policy.snap | 6 +- crates/squawk_fmt/tests/after/from.snap | 54 ++++---- .../squawk_fmt/tests/after/graph_table.snap | 120 +++++++++--------- crates/squawk_fmt/tests/after/insert.snap | 6 + .../squawk_fmt/tests/after/select_expr.snap | 15 +++ crates/squawk_fmt/tests/before/insert.sql | 6 + .../squawk_fmt/tests/before/select_expr.sql | 4 + .../squawk_syntax/src/ast/generated/nodes.rs | 48 +++++++ crates/squawk_syntax/src/postgresql.ungram | 12 ++ 11 files changed, 270 insertions(+), 116 deletions(-) diff --git a/crates/squawk_fmt/src/fmt.rs b/crates/squawk_fmt/src/fmt.rs index 8c4fe003..35c0c4f8 100644 --- a/crates/squawk_fmt/src/fmt.rs +++ b/crates/squawk_fmt/src/fmt.rs @@ -349,10 +349,32 @@ fn build_insert<'a>(insert: &ast::Insert) -> Doc<'a> { .append(build_overriding_clause(overriding)); } if let Some(source) = insert.insert_source() { - doc = doc - .append(Doc::line_or_space()) - .append(leading_comments(source.syntax())) - .append(build_insert_source(source)); + let source_comments = leading_comments(source.syntax()); + match source { + ast::InsertSource::SelectVariant(ast::SelectVariant::Values(values)) + if values + .row_list() + .is_some_and(|rows| rows.rows().count() > 1) => + { + doc = doc + .append(Doc::line_or_space()) + .append(source_comments) + .append(Doc::text("values")) + .group() + .append(build_values_rows(&values, true)); + for clause in values.tail_clauses() { + doc = doc + .append(Doc::line_or_space()) + .append(build_select_tail_clause(clause)); + } + } + source => { + doc = doc + .append(Doc::line_or_space()) + .append(source_comments) + .append(build_insert_source(source)); + } + } } if let Some(on_conflict) = insert.on_conflict_clause() { doc = doc @@ -10123,7 +10145,24 @@ fn build_values<'a>(values: &ast::Values) -> Doc<'a> { } } - let mut values_doc = Doc::text("values"); + doc = doc.append( + Doc::text("values") + .append(build_values_rows(values, false)) + .group(), + ); + + for clause in values.tail_clauses() { + doc = doc + .append(Doc::line_or_space()) + .append(build_select_tail_clause(clause)); + } + + doc.append(build_semicolon(values.semicolon_token())) + .group() +} + +fn build_values_rows<'a>(values: &ast::Values, nest_rows: bool) -> Doc<'a> { + let mut doc = Doc::nil(); if let Some(row_list) = values.row_list() { let rows = row_list.rows().map(|row| { ( @@ -10132,23 +10171,23 @@ fn build_values<'a>(values: &ast::Values) -> Doc<'a> { ) }); if let Some(rows) = build_comma_separated_docs(rows) { - values_doc = values_doc.append( + let multiple_rows = row_list.rows().count() > 1; + let separator = if nest_rows && multiple_rows { + Doc::line_or_space() + } else { Doc::space() - .append(leading_comments(row_list.syntax())) - .append(rows), - ); + }; + let rows = separator + .append(leading_comments(row_list.syntax())) + .append(rows); + doc = doc.append(if nest_rows && multiple_rows { + rows.nest(2).group() + } else { + rows + }); } } - doc = doc.append(values_doc.group()); - - for clause in values.tail_clauses() { - doc = doc - .append(Doc::line_or_space()) - .append(build_select_tail_clause(clause)); - } - - doc.append(build_semicolon(values.semicolon_token())) - .group() + doc } fn build_row<'a>(row: ast::Row) -> Doc<'a> { @@ -17976,10 +18015,12 @@ fn build_select_doc_ungrouped<'a>(select: &ast::Select) -> Doc<'a> { fn build_from_clause<'a>(from: ast::FromClause) -> Doc<'a> { let mut single_item = from.items(); - let single_json_table = matches!( + let single_nested_table = matches!( single_item.next(), Some(ast::FromListItem::FromItem( - ast::FromItem::JsonTableFromItem(_) + ast::FromItem::GraphTableFromItem(_) + | ast::FromItem::JsonTableFromItem(_) + | ast::FromItem::XmlTableFromItem(_) )) ) && single_item.next().is_none(); @@ -17991,7 +18032,7 @@ fn build_from_clause<'a>(from: ast::FromClause) -> Doc<'a> { ) }); let body = build_comma_separated_docs(items).unwrap_or_else(Doc::nil); - let body = if single_json_table { + let body = if single_nested_table { body } else { body.nest(2) @@ -19319,7 +19360,7 @@ fn build_expr<'a>(expr: ast::Expr) -> Doc<'a> { ast::Expr::FieldExpr(field_expr) => build_field_expr(field_expr), ast::Expr::IndexExpr(index_expr) => build_index_expr(index_expr), ast::Expr::Literal(literal) => build_literal(literal), - ast::Expr::NameRef(name_ref) => build_name(name_ref.syntax()), + ast::Expr::NameRef(name_ref) => build_expr_name_ref(name_ref), ast::Expr::ParenExpr(paren_expr) => build_paren_expr(paren_expr), ast::Expr::PostfixExpr(postfix_expr) => build_postfix_expr(postfix_expr), ast::Expr::PrefixExpr(prefix_expr) => build_prefix_expr(prefix_expr), @@ -19328,6 +19369,28 @@ fn build_expr<'a>(expr: ast::Expr) -> Doc<'a> { } } +fn build_expr_name_ref<'a>(name_ref: ast::NameRef) -> Doc<'a> { + let sql_value_function = name_ref + .current_catalog_token() + .or_else(|| name_ref.current_date_token()) + .or_else(|| name_ref.current_role_token()) + .or_else(|| name_ref.current_schema_token()) + .or_else(|| name_ref.current_time_token()) + .or_else(|| name_ref.current_timestamp_token()) + .or_else(|| name_ref.current_user_token()) + .or_else(|| name_ref.localtime_token()) + .or_else(|| name_ref.localtimestamp_token()) + .or_else(|| name_ref.session_user_token()) + .or_else(|| name_ref.system_user_token()) + .or_else(|| name_ref.user_token()); + + if let Some(token) = sql_value_function { + Doc::text(token.text().to_ascii_lowercase()) + } else { + build_name(name_ref.syntax()) + } +} + fn build_array_expr<'a>(array_expr: ast::ArrayExpr) -> Doc<'a> { let mut doc = Doc::nil(); diff --git a/crates/squawk_fmt/tests/after/alter_policy.snap b/crates/squawk_fmt/tests/after/alter_policy.snap index 95c757b6..67fb661a 100644 --- a/crates/squawk_fmt/tests/after/alter_policy.snap +++ b/crates/squawk_fmt/tests/after/alter_policy.snap @@ -7,7 +7,7 @@ alter /* policy */ policy /* name */ account_access_policy /* to */ to /* first role */ account_administrator, /* second role */ account_auditor /* using */ using /* left paren */ ( - /* expression */ owner_identifier = "current_user" /* right paren */ + /* expression */ owner_identifier = current_user /* right paren */ ) /* with */ with /* check */ check /* left paren */ ( /* expression */ account_is_active @@ -19,8 +19,8 @@ alter policy account_access_policy using ( account_identifier = a_very_long_session_account_identifier_function( - "current_user", - "current_role" + current_user, + current_role ) ); diff --git a/crates/squawk_fmt/tests/after/create_policy.snap b/crates/squawk_fmt/tests/after/create_policy.snap index 0b015271..916d800e 100644 --- a/crates/squawk_fmt/tests/after/create_policy.snap +++ b/crates/squawk_fmt/tests/after/create_policy.snap @@ -11,9 +11,9 @@ create policy extraordinarily_long_account_access_policy_name to extraordinarily_long_account_administrator_role, extraordinarily_long_account_auditor_role using ( - account_owner_identifier = "current_user" and account_identifier > 1000000 + account_owner_identifier = current_user and account_identifier > 1000000 ) - with check (account_is_active and account_owner_identifier = "current_user"); + with check (account_is_active and account_owner_identifier = current_user); -- comments in every position create /* policy */ policy /* name */ account_access @@ -23,7 +23,7 @@ create /* policy */ policy /* name */ account_access /* to */ to /* first role */ account_admin /* comma */, /* second role */ account_auditor /* using */ using /* left paren */ ( - /* expression */ owner_name = "current_user" /* right paren */ + /* expression */ owner_name = current_user /* right paren */ ) /* with */ with /* check */ check /* check left paren */ ( /* check expression */ active /* check right paren */ diff --git a/crates/squawk_fmt/tests/after/from.snap b/crates/squawk_fmt/tests/after/from.snap index 60e1854f..49a2663f 100644 --- a/crates/squawk_fmt/tests/after/from.snap +++ b/crates/squawk_fmt/tests/after/from.snap @@ -76,37 +76,37 @@ from /* before lateral */ lateral /* before rows */ rows /* before from */ from ); select * from xmltable( - '/rows/row' passing doc - columns - id int8 path '@id' not null, - ord for ordinality, - value text default 'unknown' null - ) as parsed; + '/rows/row' passing doc + columns + id int8 path '@id' not null, + ord for ordinality, + value text default 'unknown' null +) as parsed; select * from lateral xmltable( - xmlnamespaces('urn:a' as a, default 'urn:default'), - a_very_long_xml_row_expression passing by ref - a_very_long_xml_document_expression by value - columns - a_very_long_first_xml_column_name a_very_long_xml_column_type - path a_very_long_xml_path_expression, - a_very_long_ordinality_column_name for ordinality - ) as a_very_long_xml_table_alias; + xmlnamespaces('urn:a' as a, default 'urn:default'), + a_very_long_xml_row_expression passing by ref + a_very_long_xml_document_expression by value + columns + a_very_long_first_xml_column_name a_very_long_xml_column_type + path a_very_long_xml_path_expression, + a_very_long_ordinality_column_name for ordinality +) as a_very_long_xml_table_alias; select * from /* before lateral */ lateral /* before xmltable */ xmltable /* before opening paren */( - /* before namespaces */ xmlnamespaces /* before namespace opening paren */( - /* before namespace expression */ 'urn:a' /* before as */ as /* before prefix */ a /* before namespace comma */, - /* before default */ default /* before default expression */ 'urn:default' /* before namespace closing paren */ - ) /* before outer comma */, - /* before row */ '/rows/row' - /* before passing */ passing /* before first by */ by /* before ref */ ref - /* before document */ doc /* before second by */ by /* before value */ value - /* before columns */ columns - /* before first column */ id /* before type */ int8 - /* before path */ path /* before path expression */ '@id' - /* before not */ not /* before null */ null /* before column comma */, - /* before ordinality column */ ord /* before for */ for /* before ordinality */ ordinality /* before closing paren */ - ) /* before alias */ as /* before alias name */ parsed; + /* before namespaces */ xmlnamespaces /* before namespace opening paren */( + /* before namespace expression */ 'urn:a' /* before as */ as /* before prefix */ a /* before namespace comma */, + /* before default */ default /* before default expression */ 'urn:default' /* before namespace closing paren */ + ) /* before outer comma */, + /* before row */ '/rows/row' + /* before passing */ passing /* before first by */ by /* before ref */ ref + /* before document */ doc /* before second by */ by /* before value */ value + /* before columns */ columns + /* before first column */ id /* before type */ int8 + /* before path */ path /* before path expression */ '@id' + /* before not */ not /* before null */ null /* before column comma */, + /* before ordinality column */ ord /* before for */ for /* before ordinality */ ordinality /* before closing paren */ +) /* before alias */ as /* before alias name */ parsed; select * from json_table( doc, diff --git a/crates/squawk_fmt/tests/after/graph_table.snap b/crates/squawk_fmt/tests/after/graph_table.snap index 32e0ad35..7ee54714 100644 --- a/crates/squawk_fmt/tests/after/graph_table.snap +++ b/crates/squawk_fmt/tests/after/graph_table.snap @@ -6,75 +6,75 @@ select * from graph_table(g match (a) columns (a)); select * from lateral graph_table( - public.g - match - (a is person)-[e is knows where e.weight > 1]->(b) - where a.active - columns (a.name as person_name, b.name) - ) as matches; + public.g + match + (a is person)-[e is knows where e.weight > 1]->(b) + where a.active + columns (a.name as person_name, b.name) +) as matches; select * from graph_table( - g - match - (a)<-[left_edge]-(b), - (c)-[any_edge]-(d), - (e)<-(f), - (h)->(i), - (j)-(k), - ((x)->(y) where x.active) - columns (a) - ); + g + match + (a)<-[left_edge]-(b), + (c)-[any_edge]-(d), + (e)<-(f), + (h)->(i), + (j)-(k), + ((x)->(y) where x.active) + columns (a) +); select * from graph_table(g match (a)->{1}(b), (c)-{, 3}(d), (e)-{2, 4}(f) columns (a)); select * from graph_table( - a_very_long_property_graph_name - match - (a_very_long_vertex_variable - is a_very_long_vertex_label - where a_very_long_vertex_filter_expression) - -[a_very_long_edge_variable - is a_very_long_edge_label - where a_very_long_edge_filter_expression]-> - (a_second_very_long_vertex_variable is a_second_very_long_vertex_label), - (a_third_very_long_vertex_variable) - -[a_second_very_long_edge_variable]- - (a_fourth_very_long_vertex_variable) - where a_very_long_graph_filter_expression - columns ( - a_very_long_vertex_variable.long_property_name as first_long_output_column_name, - a_second_very_long_vertex_variable.other_long_property_name as second_output_column_name - ) - ); + a_very_long_property_graph_name + match + (a_very_long_vertex_variable + is a_very_long_vertex_label + where a_very_long_vertex_filter_expression) + -[a_very_long_edge_variable + is a_very_long_edge_label + where a_very_long_edge_filter_expression]-> + (a_second_very_long_vertex_variable is a_second_very_long_vertex_label), + (a_third_very_long_vertex_variable) + -[a_second_very_long_edge_variable]- + (a_fourth_very_long_vertex_variable) + where a_very_long_graph_filter_expression + columns ( + a_very_long_vertex_variable.long_property_name as first_long_output_column_name, + a_second_very_long_vertex_variable.other_long_property_name as second_output_column_name + ) +); select * from /* before graph table */ graph_table /* before outer opening paren */( - /* before graph */ public /* before graph dot */./* before graph name */ g - /* before match */ match - /* before first vertex opening paren */ (/* before first variable */ a - /* before is */ is /* before first label */ person - /* before vertex where */ where - /* before vertex expression */ a.active /* before first vertex closing paren */) - /* before first edge minus */ - /* before edge opening bracket */[/* before edge variable */ e - /* before edge is */ is /* before edge label */ knows - /* before edge where */ where - /* before edge expression */ e.active /* before edge closing bracket */] /* before edge ending minus */- /* before right angle */> - /* before second vertex */ (b)/* before qualifier opening curly */ {/* before qualifier min */ 1 /* before qualifier comma */, /* before qualifier max */ 3 /* before qualifier closing curly */} /* before pattern comma */, - /* before second pattern */ /* before left angle */ < /* before left minus */- /* before left opening bracket */[left_edge /* before left closing bracket */] /* before left ending minus */- - (c), - (d) - /* before any edge */ - /* before any opening bracket */[any_edge /* before any closing bracket */] /* before any ending minus */- - (e), - /* before nested opening paren */ (/* before nested pattern */ (x) - /* before simple edge */ - /* before simple right angle */> - (y) /* before nested where */ where - /* before nested expression */ x.active /* before nested closing paren */) - /* before graph where */ where /* before graph expression */ b.active - /* before columns */ columns /* before columns opening paren */( - /* before first column */ a.name /* before column as */ as /* before column name */ source /* before column comma */, - /* before second column */ b.name /* before columns closing paren */ - ) /* before outer closing paren */ - ) /* after graph table */ as /* before alias */ result; + /* before graph */ public /* before graph dot */./* before graph name */ g + /* before match */ match + /* before first vertex opening paren */ (/* before first variable */ a + /* before is */ is /* before first label */ person + /* before vertex where */ where + /* before vertex expression */ a.active /* before first vertex closing paren */) + /* before first edge minus */ - /* before edge opening bracket */[/* before edge variable */ e + /* before edge is */ is /* before edge label */ knows + /* before edge where */ where + /* before edge expression */ e.active /* before edge closing bracket */] /* before edge ending minus */- /* before right angle */> + /* before second vertex */ (b)/* before qualifier opening curly */ {/* before qualifier min */ 1 /* before qualifier comma */, /* before qualifier max */ 3 /* before qualifier closing curly */} /* before pattern comma */, + /* before second pattern */ /* before left angle */ < /* before left minus */- /* before left opening bracket */[left_edge /* before left closing bracket */] /* before left ending minus */- + (c), + (d) + /* before any edge */ - /* before any opening bracket */[any_edge /* before any closing bracket */] /* before any ending minus */- + (e), + /* before nested opening paren */ (/* before nested pattern */ (x) + /* before simple edge */ - /* before simple right angle */> + (y) /* before nested where */ where + /* before nested expression */ x.active /* before nested closing paren */) + /* before graph where */ where /* before graph expression */ b.active + /* before columns */ columns /* before columns opening paren */( + /* before first column */ a.name /* before column as */ as /* before column name */ source /* before column comma */, + /* before second column */ b.name /* before columns closing paren */ + ) /* before outer closing paren */ +) /* after graph table */ as /* before alias */ result; diff --git a/crates/squawk_fmt/tests/after/insert.snap b/crates/squawk_fmt/tests/after/insert.snap index 8e24cd1f..b4dc339b 100644 --- a/crates/squawk_fmt/tests/after/insert.snap +++ b/crates/squawk_fmt/tests/after/insert.snap @@ -20,6 +20,12 @@ with inserted as ( select * from inserted; +insert into products (product_no, name, price) values + (1, 'Cheese', 9.99), + (2, 'Bread', 1.99), + (3, 'Milk', 2.99), + (4, 'Eggs', 3.99); + with inserted as ( insert into a_very_long_schema_name.a_very_long_table_name ( organization_identifier, diff --git a/crates/squawk_fmt/tests/after/select_expr.snap b/crates/squawk_fmt/tests/after/select_expr.snap index 5eb3aa11..adb3f363 100644 --- a/crates/squawk_fmt/tests/after/select_expr.snap +++ b/crates/squawk_fmt/tests/after/select_expr.snap @@ -814,3 +814,18 @@ select json_object( with unique keys returning jsonb format json ); + +select foo = current_date; + +select + current_catalog, + current_role, + current_schema, + current_time(2), + current_timestamp(3), + current_user, + localtime(4), + localtimestamp(5), + session_user, + system_user, + user; diff --git a/crates/squawk_fmt/tests/before/insert.sql b/crates/squawk_fmt/tests/before/insert.sql index f2a1e927..5d89c878 100644 --- a/crates/squawk_fmt/tests/before/insert.sql +++ b/crates/squawk_fmt/tests/before/insert.sql @@ -4,6 +4,12 @@ WITH inserted AS (INSERT INTO foo DEFAULT VALUES RETURNING id) SELECT * FROM ins WITH inserted AS (INSERT INTO foo AS f (id, name) OVERRIDING SYSTEM VALUE SELECT id, name FROM incoming ON CONFLICT ON CONSTRAINT foo_pkey DO NOTHING RETURNING id) SELECT * FROM inserted; +insert into products (product_no, name, price) +values (1, 'Cheese', 9.99), +(2, 'Bread', 1.99), +(3, 'Milk', 2.99), + (4, 'Eggs', 3.99); + WITH inserted AS (INSERT INTO a_very_long_schema_name.a_very_long_table_name (organization_identifier, extremely_long_descriptive_column_name) VALUES (123456789, 'an extremely long value that forces the insert statement to wrap across lines') ON CONFLICT (organization_identifier) DO UPDATE SET extremely_long_descriptive_column_name = excluded.extremely_long_descriptive_column_name WHERE a_very_long_table_name.organization_identifier = excluded.organization_identifier RETURNING organization_identifier, extremely_long_descriptive_column_name) SELECT organization_identifier, extremely_long_descriptive_column_name FROM inserted; /*before*/ WITH /*a*/ inserted /*b*/ (/*c*/ result_id /*d*/) /*e*/ AS /*f*/ NOT /*g*/ MATERIALIZED /*h*/ (/*i*/ INSERT /*j*/ INTO /*k*/ public /*l*/ . /*m*/ foo /*n*/ AS /*o*/ f /*p*/ (/*q*/ id /*r*/, /*s*/ payload /*t*/) /*u*/ OVERRIDING /*v*/ USER /*w*/ VALUE /*x*/ VALUES /*y*/ (/*z*/ 1 /*aa*/, /*ab*/ 'new' /*ac*/) /*ad*/ ON /*ae*/ CONFLICT /*af*/ (/*ag*/ id /*ah*/ COLLATE /*ai*/ "C" /*aj*/ text_ops /*ak*/) /*al*/ WHERE /*am*/ id > 0 /*an*/ DO /*ao*/ UPDATE /*ap*/ SET /*aq*/ payload /*ar*/ = /*as*/ excluded.payload /*at*/ WHERE /*au*/ foo.id = excluded.id /*av*/ RETURNING /*aw*/ WITH /*ax*/ (/*ay*/ OLD /*az*/ AS /*ba*/ old_row /*bb*/, /*bc*/ NEW /*bd*/ AS /*be*/ new_row /*bf*/) /*bg*/ new_row.id /*bh*/) /*bi*/ SELECT /*bj*/ result_id /*bk*/ FROM /*bl*/ inserted /*bm*/; diff --git a/crates/squawk_fmt/tests/before/select_expr.sql b/crates/squawk_fmt/tests/before/select_expr.sql index 9dbd2272..9b75654a 100644 --- a/crates/squawk_fmt/tests/before/select_expr.sql +++ b/crates/squawk_fmt/tests/before/select_expr.sql @@ -338,3 +338,7 @@ select json_object( 'a': 1, 'b' value 2 format json null on null with unique keys returning jsonb format json); + +select foo = current_date; + +select current_catalog, current_role, current_schema, current_time(2), current_timestamp(3), current_user, localtime(4), localtimestamp(5), session_user, system_user, user; diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index 2f19f56c..a36af13b 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -15661,10 +15661,58 @@ pub struct NameRef { pub(crate) syntax: SyntaxNode, } impl NameRef { + #[inline] + pub fn current_catalog_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_CATALOG_KW) + } + #[inline] + pub fn current_date_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_DATE_KW) + } + #[inline] + pub fn current_role_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_ROLE_KW) + } + #[inline] + pub fn current_schema_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_SCHEMA_KW) + } + #[inline] + pub fn current_time_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_TIME_KW) + } + #[inline] + pub fn current_timestamp_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_TIMESTAMP_KW) + } + #[inline] + pub fn current_user_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CURRENT_USER_KW) + } #[inline] pub fn ident_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::IDENT) } + #[inline] + pub fn localtime_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LOCALTIME_KW) + } + #[inline] + pub fn localtimestamp_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LOCALTIMESTAMP_KW) + } + #[inline] + pub fn session_user_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::SESSION_USER_KW) + } + #[inline] + pub fn system_user_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::SYSTEM_USER_KW) + } + #[inline] + pub fn user_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::USER_KW) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 7902135d..1790408a 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -17,6 +17,18 @@ NameRef = '#name'? +| 'current_catalog' +| 'current_date' +| 'current_role' +| 'current_schema' +| 'current_time' +| 'current_timestamp' +| 'current_user' +| 'localtime' +| 'localtimestamp' +| 'session_user' +| 'system_user' +| 'user' PathSegment = '#name'?