diff --git a/crates/squawk_ide/src/classify.rs b/crates/squawk_ide/src/classify.rs index 1cd1a38f..1a8d594a 100644 --- a/crates/squawk_ide/src/classify.rs +++ b/crates/squawk_ide/src/classify.rs @@ -89,6 +89,7 @@ fn is_special_fn(kind: SyntaxKind) -> bool { | SyntaxKind::ANY_FN | SyntaxKind::ALL_FN | SyntaxKind::EXISTS_FN + | SyntaxKind::GRAPH_TABLE_FN ) } diff --git a/crates/squawk_ide/src/expand_selection.rs b/crates/squawk_ide/src/expand_selection.rs index 110a5f98..ccef3110 100644 --- a/crates/squawk_ide/src/expand_selection.rs +++ b/crates/squawk_ide/src/expand_selection.rs @@ -59,7 +59,7 @@ const DELIMITED_LIST_KINDS: &[SyntaxKind] = &[ SyntaxKind::REVOKE_COMMAND_LIST, SyntaxKind::ROLE_REF_LIST, SyntaxKind::ROW_LIST, - SyntaxKind::XML_ATTRIBUTE_LIST, + SyntaxKind::EXPR_AS_NAME_LIST, SyntaxKind::XML_NAMESPACE_LIST, SyntaxKind::SET_COLUMN_LIST, SyntaxKind::SET_EXPR_LIST, @@ -73,6 +73,8 @@ const DELIMITED_LIST_KINDS: &[SyntaxKind] = &[ SyntaxKind::VACUUM_OPTION_LIST, SyntaxKind::VARIANT_LIST, SyntaxKind::XML_TABLE_COLUMN_LIST, + SyntaxKind::PATH_PATTERN_LIST, + SyntaxKind::PROPERTIES_LIST, ]; pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> TextRange { @@ -553,6 +555,7 @@ $0 SyntaxKind::TRIGGER_EVENT_LIST, SyntaxKind::XML_COLUMN_OPTION_LIST, SyntaxKind::WHEN_CLAUSE_LIST, + SyntaxKind::LABEL_AND_PROPERTIES_LIST, ]; let unhandled_list_kinds = (0..SyntaxKind::__LAST as u16) diff --git a/crates/squawk_ide/src/folding_ranges.rs b/crates/squawk_ide/src/folding_ranges.rs index 00c4ce45..1e5ae92b 100644 --- a/crates/squawk_ide/src/folding_ranges.rs +++ b/crates/squawk_ide/src/folding_ranges.rs @@ -156,10 +156,13 @@ fn fold_kind(kind: SyntaxKind) -> Option { | SyntaxKind::TRIGGER_EVENT_LIST | SyntaxKind::VACUUM_OPTION_LIST | SyntaxKind::VARIANT_LIST - | SyntaxKind::XML_ATTRIBUTE_LIST + | SyntaxKind::EXPR_AS_NAME_LIST | SyntaxKind::XML_COLUMN_OPTION_LIST | SyntaxKind::XML_NAMESPACE_LIST - | SyntaxKind::XML_TABLE_COLUMN_LIST => Some(FoldKind::List), + | SyntaxKind::XML_TABLE_COLUMN_LIST + | SyntaxKind::LABEL_AND_PROPERTIES_LIST + | SyntaxKind::PATH_PATTERN_LIST + | SyntaxKind::PROPERTIES_LIST => Some(FoldKind::List), _ => None, } } diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index f20ed8d7..2c72657c 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -2318,13 +2318,13 @@ drop type person$0; fn goto_create_table_type_reference() { assert_snapshot!(goto(" create type person_info as (name text, email text); -create table user(id int, member person_info$0); -"), @r" +create table users(id int, member person_info$0); +"), @" ╭▸ 2 │ create type person_info as (name text, email text); │ ─────────── 2. destination - 3 │ create table user(id int, member person_info); - ╰╴ ─ 1. source + 3 │ create table users(id int, member person_info); + ╰╴ ─ 1. source "); } @@ -2416,14 +2416,14 @@ create table data(id int, value myint$0); fn goto_composite_type_field() { assert_snapshot!(goto(" create type person_info as (name text, email text); -create table user(id int, member person_info); -select (member).name$0 from user; -"), @r" +create table users(id int, member person_info); +select (member).name$0 from users; +"), @" ╭▸ 2 │ create type person_info as (name text, email text); │ ──── 2. destination - 3 │ create table user(id int, member person_info); - 4 │ select (member).name from user; + 3 │ create table users(id int, member person_info); + 4 │ select (member).name from users; ╰╴ ─ 1. source "); } diff --git a/crates/squawk_lexer/src/lib.rs b/crates/squawk_lexer/src/lib.rs index 3f6521ed..24522d8d 100644 --- a/crates/squawk_lexer/src/lib.rs +++ b/crates/squawk_lexer/src/lib.rs @@ -100,6 +100,8 @@ impl Cursor<'_> { ')' => TokenKind::CloseParen, '[' => TokenKind::OpenBracket, ']' => TokenKind::CloseBracket, + '{' => TokenKind::OpenCurly, + '}' => TokenKind::CloseCurly, '@' => TokenKind::At, '#' => TokenKind::Pound, '~' => TokenKind::Tilde, diff --git a/crates/squawk_lexer/src/token.rs b/crates/squawk_lexer/src/token.rs index a768fd66..525e84a0 100644 --- a/crates/squawk_lexer/src/token.rs +++ b/crates/squawk_lexer/src/token.rs @@ -69,6 +69,10 @@ pub enum TokenKind { CloseBracket, /// `[` OpenBracket, + /// `}` + CloseCurly, + /// `{` + OpenCurly, /// `)` CloseParen, /// `(` diff --git a/crates/squawk_linter/src/analyze.rs b/crates/squawk_linter/src/analyze.rs index 4c165725..a496acb4 100644 --- a/crates/squawk_linter/src/analyze.rs +++ b/crates/squawk_linter/src/analyze.rs @@ -52,6 +52,7 @@ pub fn possibly_slow_stmt(stmt: &ast::Stmt) -> bool { | ast::Stmt::AlterOperatorClass(_) | ast::Stmt::AlterOperatorFamily(_) | ast::Stmt::AlterPolicy(_) + | ast::Stmt::AlterPropertyGraph(_) | ast::Stmt::AlterProcedure(_) | ast::Stmt::AlterPublication(_) | ast::Stmt::AlterRole(_) @@ -94,6 +95,7 @@ pub fn possibly_slow_stmt(stmt: &ast::Stmt) -> bool { | ast::Stmt::CreateOperatorClass(_) | ast::Stmt::CreateOperatorFamily(_) | ast::Stmt::CreatePolicy(_) + | ast::Stmt::CreatePropertyGraph(_) | ast::Stmt::CreateProcedure(_) | ast::Stmt::CreatePublication(_) | ast::Stmt::CreateRole(_) @@ -136,6 +138,7 @@ pub fn possibly_slow_stmt(stmt: &ast::Stmt) -> bool { | ast::Stmt::DropOperatorFamily(_) | ast::Stmt::DropOwned(_) | ast::Stmt::DropPolicy(_) + | ast::Stmt::DropPropertyGraph(_) | ast::Stmt::DropProcedure(_) | ast::Stmt::DropPublication(_) | ast::Stmt::DropRole(_) @@ -197,6 +200,7 @@ pub fn possibly_slow_stmt(stmt: &ast::Stmt) -> bool { | ast::Stmt::ReleaseSavepoint(_) | ast::Stmt::Reset(_) | ast::Stmt::Revoke(_) + | ast::Stmt::Repack(_) | ast::Stmt::Rollback(_) | ast::Stmt::Savepoint(_) | ast::Stmt::SecurityLabel(_) diff --git a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs index ac816b37..f1eb20bd 100644 --- a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs +++ b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs @@ -277,10 +277,10 @@ DROP TABLE users; #[test] fn fix_create_index_if_not_exists() { assert_snapshot!(fix(" -create index idx on table (col); +create index idx on items (col); CREATE INDEX CONCURRENTLY idx2 ON users (email); "), @r" - create index if not exists idx on table (col); + create index if not exists idx on items (col); CREATE INDEX CONCURRENTLY if not exists idx2 ON users (email); "); } diff --git a/crates/squawk_parser/src/generated/syntax_kind.rs b/crates/squawk_parser/src/generated/syntax_kind.rs index 0f9e1296..1d6b8e7f 100644 --- a/crates/squawk_parser/src/generated/syntax_kind.rs +++ b/crates/squawk_parser/src/generated/syntax_kind.rs @@ -17,6 +17,8 @@ pub enum SyntaxKind { R_PAREN, L_BRACK, R_BRACK, + L_CURLY, + R_CURLY, L_ANGLE, R_ANGLE, AT, @@ -148,6 +150,7 @@ pub enum SyntaxKind { DEPENDS_KW, DEPTH_KW, DESC_KW, + DESTINATION_KW, DETACH_KW, DICTIONARY_KW, DISABLE_KW, @@ -159,6 +162,7 @@ pub enum SyntaxKind { DOUBLE_KW, DROP_KW, EACH_KW, + EDGE_KW, ELSE_KW, EMPTY_KW, ENABLE_KW, @@ -203,6 +207,8 @@ pub enum SyntaxKind { GLOBAL_KW, GRANT_KW, GRANTED_KW, + GRAPH_KW, + GRAPH_TABLE_KW, GREATEST_KW, GROUP_KW, GROUPING_KW, @@ -309,6 +315,7 @@ pub enum SyntaxKind { NFKC_KW, NFKD_KW, NO_KW, + NODE_KW, NONE_KW, NORMALIZE_KW, NORMALIZED_KW, @@ -373,6 +380,8 @@ pub enum SyntaxKind { PROCEDURE_KW, PROCEDURES_KW, PROGRAM_KW, + PROPERTIES_KW, + PROPERTY_KW, PUBLICATION_KW, QUOTE_KW, QUOTES_KW, @@ -386,9 +395,11 @@ pub enum SyntaxKind { REFERENCING_KW, REFRESH_KW, REINDEX_KW, + RELATIONSHIP_KW, RELATIVE_KW, RELEASE_KW, RENAME_KW, + REPACK_KW, REPEATABLE_KW, REPLACE_KW, REPLICA_KW, @@ -507,6 +518,7 @@ pub enum SyntaxKind { VARYING_KW, VERBOSE_KW, VERSION_KW, + VERTEX_KW, VIEW_KW, VIEWS_KW, VIRTUAL_KW, @@ -555,12 +567,16 @@ pub enum SyntaxKind { ADD_COLUMN, ADD_CONSTRAINT, ADD_GENERATED, + ADD_LABEL, ADD_OP_CLASS_OPTIONS, ADD_VALUE, + ADD_VERTEX_EDGE_LABEL_PROPERTIES, + ADD_VERTEX_EDGE_TABLES, AFTER_VALUE, AGGREGATE, ALIAS, ALL_FN, + ALL_PROPERTIES, ALTER_AGGREGATE, ALTER_ATTRIBUTE, ALTER_COLLATION, @@ -587,6 +603,7 @@ pub enum SyntaxKind { ALTER_OPTION_LIST, ALTER_POLICY, ALTER_PROCEDURE, + ALTER_PROPERTY_GRAPH, ALTER_PUBLICATION, ALTER_ROLE, ALTER_ROUTINE, @@ -608,6 +625,7 @@ pub enum SyntaxKind { ALTER_TYPE, ALTER_USER, ALTER_USER_MAPPING, + ALTER_VERTEX_EDGE_LABELS, ALTER_VIEW, ANALYZE, ANY_FN, @@ -692,6 +710,7 @@ pub enum SyntaxKind { CREATE_OPERATOR_FAMILY, CREATE_POLICY, CREATE_PROCEDURE, + CREATE_PROPERTY_GRAPH, CREATE_PUBLICATION, CREATE_ROLE, CREATE_RULE, @@ -722,6 +741,7 @@ pub enum SyntaxKind { DELETE, DELETE_ROWS, DEPENDS_ON_EXTENSION, + DEST_VERTEX_TABLE, DETACH_PARTITION, DISABLE_RLS, DISABLE_RULE, @@ -742,6 +762,7 @@ pub enum SyntaxKind { DROP_DATABASE, DROP_DEFAULT, DROP_DOMAIN, + DROP_EDGE_TABLES, DROP_EVENT_TRIGGER, DROP_EXPRESSION, DROP_EXTENSION, @@ -763,6 +784,7 @@ pub enum SyntaxKind { DROP_OWNED, DROP_POLICY, DROP_PROCEDURE, + DROP_PROPERTY_GRAPH, DROP_PUBLICATION, DROP_ROLE, DROP_ROUTINE, @@ -783,7 +805,15 @@ pub enum SyntaxKind { DROP_TYPE, DROP_USER, DROP_USER_MAPPING, + DROP_VERTEX_EDGE_LABEL, + DROP_VERTEX_EDGE_LABEL_PROPERTIES, + DROP_VERTEX_TABLES, DROP_VIEW, + EDGE_ANY, + EDGE_LEFT, + EDGE_RIGHT, + EDGE_TABLES, + EDGE_TABLE_DEF, ELSE_CLAUSE, ENABLE_ALWAYS_RULE, ENABLE_ALWAYS_TRIGGER, @@ -802,6 +832,7 @@ pub enum SyntaxKind { EXISTS_FN, EXPLAIN, EXPR_AS_NAME, + EXPR_AS_NAME_LIST, EXPR_TYPE, EXTRACT_FN, FAT_ARROW, @@ -824,6 +855,8 @@ pub enum SyntaxKind { GENERATED_CONSTRAINT, GRANT, GRANT_DEFAULT_PRIVILEGES, + GRAPH_PATTERN_QUALIFIER, + GRAPH_TABLE_FN, GROUPING_CUBE, GROUPING_EXPR, GROUPING_ROLLUP, @@ -852,6 +885,7 @@ pub enum SyntaxKind { IS_JSON_OBJECT, IS_JSON_SCALAR, IS_JSON_VALUE, + IS_LABEL_EXPRESSION, IS_NORMALIZED, IS_NOT, IS_NOT_DISTINCT_FROM, @@ -907,6 +941,8 @@ pub enum SyntaxKind { JSON_VALUE_EXPR, JSON_VALUE_FN, JSON_WRAPPER_BEHAVIOR_CLAUSE, + LABEL_AND_PROPERTIES, + LABEL_AND_PROPERTIES_LIST, LANGUAGE_FUNC_OPTION, LEAKPROOF_FUNC_OPTION, LIKE_CLAUSE, @@ -956,6 +992,7 @@ pub enum SyntaxKind { NO_FORCE_RLS, NO_INHERIT, NO_INHERIT_TABLE, + NO_PROPERTIES, NULLS_DISTINCT, NULLS_FIRST, NULLS_LAST, @@ -992,6 +1029,7 @@ pub enum SyntaxKind { PARAM_OUT, PARAM_VARIADIC, PAREN_EXPR, + PAREN_GRAPH_PATTERN, PAREN_SELECT, PARTITION, PARTITION_BY, @@ -1004,6 +1042,9 @@ pub enum SyntaxKind { PARTITION_LIST, PARTITION_OF, PATH, + PATH_FACTOR, + PATH_PATTERN, + PATH_PATTERN_LIST, PATH_SEGMENT, PATH_TYPE, PERCENT_TYPE, @@ -1017,6 +1058,7 @@ pub enum SyntaxKind { PRIMARY_KEY_CONSTRAINT, PRIVILEGES, PRIVILEGE_TARGET, + PROPERTIES_LIST, PUBLICATION_OBJECT, READ_COMMITTED, READ_ONLY, @@ -1037,6 +1079,7 @@ pub enum SyntaxKind { RENAME_CONSTRAINT, RENAME_TO, RENAME_VALUE, + REPACK, REPEATABLE_CLAUSE, REPEATABLE_READ, REPLICA_IDENTITY, @@ -1119,6 +1162,7 @@ pub enum SyntaxKind { SORT_DESC, SORT_USING, SOURCE_FILE, + SOURCE_VERTEX_TABLE, SPLIT_PARTITION, STORAGE, STRICT_FUNC_OPTION, @@ -1133,6 +1177,7 @@ pub enum SyntaxKind { TABLE_LIST, TARGET, TARGET_LIST, + TEMP, TIME_TYPE, TIMING, TRANSACTION_MODE_LIST, @@ -1161,6 +1206,9 @@ pub enum SyntaxKind { VALUES, VARIANT, VARIANT_LIST, + VERTEX_PATTERN, + VERTEX_TABLES, + VERTEX_TABLE_DEF, VOLATILITY_FUNC_OPTION, WHEN_CLAUSE, WHEN_CLAUSE_LIST, @@ -1183,7 +1231,6 @@ pub enum SyntaxKind { WITH_PARAMS, WITH_TABLE, WITH_TIMEZONE, - XML_ATTRIBUTE_LIST, XML_COLUMN_OPTION, XML_COLUMN_OPTION_LIST, XML_ELEMENT_FN, @@ -1429,6 +1476,8 @@ impl SyntaxKind { SyntaxKind::DEPTH_KW } else if ident.eq_ignore_ascii_case("desc") { SyntaxKind::DESC_KW + } else if ident.eq_ignore_ascii_case("destination") { + SyntaxKind::DESTINATION_KW } else if ident.eq_ignore_ascii_case("detach") { SyntaxKind::DETACH_KW } else if ident.eq_ignore_ascii_case("dictionary") { @@ -1451,6 +1500,8 @@ impl SyntaxKind { SyntaxKind::DROP_KW } else if ident.eq_ignore_ascii_case("each") { SyntaxKind::EACH_KW + } else if ident.eq_ignore_ascii_case("edge") { + SyntaxKind::EDGE_KW } else if ident.eq_ignore_ascii_case("else") { SyntaxKind::ELSE_KW } else if ident.eq_ignore_ascii_case("empty") { @@ -1539,6 +1590,10 @@ impl SyntaxKind { SyntaxKind::GRANT_KW } else if ident.eq_ignore_ascii_case("granted") { SyntaxKind::GRANTED_KW + } else if ident.eq_ignore_ascii_case("graph") { + SyntaxKind::GRAPH_KW + } else if ident.eq_ignore_ascii_case("graph_table") { + SyntaxKind::GRAPH_TABLE_KW } else if ident.eq_ignore_ascii_case("greatest") { SyntaxKind::GREATEST_KW } else if ident.eq_ignore_ascii_case("group") { @@ -1751,6 +1806,8 @@ impl SyntaxKind { SyntaxKind::NFKD_KW } else if ident.eq_ignore_ascii_case("no") { SyntaxKind::NO_KW + } else if ident.eq_ignore_ascii_case("node") { + SyntaxKind::NODE_KW } else if ident.eq_ignore_ascii_case("none") { SyntaxKind::NONE_KW } else if ident.eq_ignore_ascii_case("normalize") { @@ -1879,6 +1936,10 @@ impl SyntaxKind { SyntaxKind::PROCEDURES_KW } else if ident.eq_ignore_ascii_case("program") { SyntaxKind::PROGRAM_KW + } else if ident.eq_ignore_ascii_case("properties") { + SyntaxKind::PROPERTIES_KW + } else if ident.eq_ignore_ascii_case("property") { + SyntaxKind::PROPERTY_KW } else if ident.eq_ignore_ascii_case("publication") { SyntaxKind::PUBLICATION_KW } else if ident.eq_ignore_ascii_case("quote") { @@ -1905,12 +1966,16 @@ impl SyntaxKind { SyntaxKind::REFRESH_KW } else if ident.eq_ignore_ascii_case("reindex") { SyntaxKind::REINDEX_KW + } else if ident.eq_ignore_ascii_case("relationship") { + SyntaxKind::RELATIONSHIP_KW } else if ident.eq_ignore_ascii_case("relative") { SyntaxKind::RELATIVE_KW } else if ident.eq_ignore_ascii_case("release") { SyntaxKind::RELEASE_KW } else if ident.eq_ignore_ascii_case("rename") { SyntaxKind::RENAME_KW + } else if ident.eq_ignore_ascii_case("repack") { + SyntaxKind::REPACK_KW } else if ident.eq_ignore_ascii_case("repeatable") { SyntaxKind::REPEATABLE_KW } else if ident.eq_ignore_ascii_case("replace") { @@ -2147,6 +2212,8 @@ impl SyntaxKind { SyntaxKind::VERBOSE_KW } else if ident.eq_ignore_ascii_case("version") { SyntaxKind::VERSION_KW + } else if ident.eq_ignore_ascii_case("vertex") { + SyntaxKind::VERTEX_KW } else if ident.eq_ignore_ascii_case("view") { SyntaxKind::VIEW_KW } else if ident.eq_ignore_ascii_case("views") { diff --git a/crates/squawk_parser/src/generated/token_sets.rs b/crates/squawk_parser/src/generated/token_sets.rs index 0e4504f1..00559bc5 100644 --- a/crates/squawk_parser/src/generated/token_sets.rs +++ b/crates/squawk_parser/src/generated/token_sets.rs @@ -84,6 +84,7 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DELIMITERS_KW, SyntaxKind::DEPENDS_KW, SyntaxKind::DEPTH_KW, + SyntaxKind::DESTINATION_KW, SyntaxKind::DETACH_KW, SyntaxKind::DICTIONARY_KW, SyntaxKind::DISABLE_KW, @@ -93,6 +94,7 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DOUBLE_KW, SyntaxKind::DROP_KW, SyntaxKind::EACH_KW, + SyntaxKind::EDGE_KW, SyntaxKind::EMPTY_KW, SyntaxKind::ENABLE_KW, SyntaxKind::ENCODING_KW, @@ -126,6 +128,8 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::GENERATED_KW, SyntaxKind::GLOBAL_KW, SyntaxKind::GRANTED_KW, + SyntaxKind::GRAPH_KW, + SyntaxKind::GRAPH_TABLE_KW, SyntaxKind::GREATEST_KW, SyntaxKind::GROUPING_KW, SyntaxKind::GROUPS_KW, @@ -213,6 +217,7 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::NFKC_KW, SyntaxKind::NFKD_KW, SyntaxKind::NO_KW, + SyntaxKind::NODE_KW, SyntaxKind::NONE_KW, SyntaxKind::NORMALIZE_KW, SyntaxKind::NORMALIZED_KW, @@ -265,6 +270,8 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::PROCEDURE_KW, SyntaxKind::PROCEDURES_KW, SyntaxKind::PROGRAM_KW, + SyntaxKind::PROPERTIES_KW, + SyntaxKind::PROPERTY_KW, SyntaxKind::PUBLICATION_KW, SyntaxKind::QUOTE_KW, SyntaxKind::QUOTES_KW, @@ -277,9 +284,11 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::REFERENCING_KW, SyntaxKind::REFRESH_KW, SyntaxKind::REINDEX_KW, + SyntaxKind::RELATIONSHIP_KW, SyntaxKind::RELATIVE_KW, SyntaxKind::RELEASE_KW, SyntaxKind::RENAME_KW, + SyntaxKind::REPACK_KW, SyntaxKind::REPEATABLE_KW, SyntaxKind::REPLACE_KW, SyntaxKind::REPLICA_KW, @@ -378,6 +387,7 @@ pub(crate) const COLUMN_OR_TABLE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::VARCHAR_KW, SyntaxKind::VARYING_KW, SyntaxKind::VERSION_KW, + SyntaxKind::VERTEX_KW, SyntaxKind::VIEW_KW, SyntaxKind::VIEWS_KW, SyntaxKind::VIRTUAL_KW, @@ -492,6 +502,7 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DELIMITERS_KW, SyntaxKind::DEPENDS_KW, SyntaxKind::DEPTH_KW, + SyntaxKind::DESTINATION_KW, SyntaxKind::DETACH_KW, SyntaxKind::DICTIONARY_KW, SyntaxKind::DISABLE_KW, @@ -501,6 +512,7 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DOUBLE_KW, SyntaxKind::DROP_KW, SyntaxKind::EACH_KW, + SyntaxKind::EDGE_KW, SyntaxKind::EMPTY_KW, SyntaxKind::ENABLE_KW, SyntaxKind::ENCODING_KW, @@ -536,6 +548,8 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::GENERATED_KW, SyntaxKind::GLOBAL_KW, SyntaxKind::GRANTED_KW, + SyntaxKind::GRAPH_KW, + SyntaxKind::GRAPH_TABLE_KW, SyntaxKind::GREATEST_KW, SyntaxKind::GROUPING_KW, SyntaxKind::GROUPS_KW, @@ -631,6 +645,7 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::NFKC_KW, SyntaxKind::NFKD_KW, SyntaxKind::NO_KW, + SyntaxKind::NODE_KW, SyntaxKind::NONE_KW, SyntaxKind::NORMALIZE_KW, SyntaxKind::NORMALIZED_KW, @@ -686,6 +701,8 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::PROCEDURE_KW, SyntaxKind::PROCEDURES_KW, SyntaxKind::PROGRAM_KW, + SyntaxKind::PROPERTIES_KW, + SyntaxKind::PROPERTY_KW, SyntaxKind::PUBLICATION_KW, SyntaxKind::QUOTE_KW, SyntaxKind::QUOTES_KW, @@ -698,9 +715,11 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::REFERENCING_KW, SyntaxKind::REFRESH_KW, SyntaxKind::REINDEX_KW, + SyntaxKind::RELATIONSHIP_KW, SyntaxKind::RELATIVE_KW, SyntaxKind::RELEASE_KW, SyntaxKind::RENAME_KW, + SyntaxKind::REPACK_KW, SyntaxKind::REPEATABLE_KW, SyntaxKind::REPLACE_KW, SyntaxKind::REPLICA_KW, @@ -803,6 +822,7 @@ pub(crate) const TYPE_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::VARYING_KW, SyntaxKind::VERBOSE_KW, SyntaxKind::VERSION_KW, + SyntaxKind::VERTEX_KW, SyntaxKind::VIEW_KW, SyntaxKind::VIEWS_KW, SyntaxKind::VIRTUAL_KW, @@ -943,6 +963,7 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DEPENDS_KW, SyntaxKind::DEPTH_KW, SyntaxKind::DESC_KW, + SyntaxKind::DESTINATION_KW, SyntaxKind::DETACH_KW, SyntaxKind::DICTIONARY_KW, SyntaxKind::DISABLE_KW, @@ -954,6 +975,7 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DOUBLE_KW, SyntaxKind::DROP_KW, SyntaxKind::EACH_KW, + SyntaxKind::EDGE_KW, SyntaxKind::ELSE_KW, SyntaxKind::EMPTY_KW, SyntaxKind::ENABLE_KW, @@ -998,6 +1020,8 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::GLOBAL_KW, SyntaxKind::GRANT_KW, SyntaxKind::GRANTED_KW, + SyntaxKind::GRAPH_KW, + SyntaxKind::GRAPH_TABLE_KW, SyntaxKind::GREATEST_KW, SyntaxKind::GROUP_KW, SyntaxKind::GROUPING_KW, @@ -1104,6 +1128,7 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::NFKC_KW, SyntaxKind::NFKD_KW, SyntaxKind::NO_KW, + SyntaxKind::NODE_KW, SyntaxKind::NONE_KW, SyntaxKind::NORMALIZE_KW, SyntaxKind::NORMALIZED_KW, @@ -1168,6 +1193,8 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::PROCEDURE_KW, SyntaxKind::PROCEDURES_KW, SyntaxKind::PROGRAM_KW, + SyntaxKind::PROPERTIES_KW, + SyntaxKind::PROPERTY_KW, SyntaxKind::PUBLICATION_KW, SyntaxKind::QUOTE_KW, SyntaxKind::QUOTES_KW, @@ -1181,9 +1208,11 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::REFERENCING_KW, SyntaxKind::REFRESH_KW, SyntaxKind::REINDEX_KW, + SyntaxKind::RELATIONSHIP_KW, SyntaxKind::RELATIVE_KW, SyntaxKind::RELEASE_KW, SyntaxKind::RENAME_KW, + SyntaxKind::REPACK_KW, SyntaxKind::REPEATABLE_KW, SyntaxKind::REPLACE_KW, SyntaxKind::REPLICA_KW, @@ -1302,6 +1331,7 @@ pub(crate) const ALL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::VARYING_KW, SyntaxKind::VERBOSE_KW, SyntaxKind::VERSION_KW, + SyntaxKind::VERTEX_KW, SyntaxKind::VIEW_KW, SyntaxKind::VIEWS_KW, SyntaxKind::VIRTUAL_KW, @@ -1440,6 +1470,7 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DEPENDS_KW, SyntaxKind::DEPTH_KW, SyntaxKind::DESC_KW, + SyntaxKind::DESTINATION_KW, SyntaxKind::DETACH_KW, SyntaxKind::DICTIONARY_KW, SyntaxKind::DISABLE_KW, @@ -1451,6 +1482,7 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DOUBLE_KW, SyntaxKind::DROP_KW, SyntaxKind::EACH_KW, + SyntaxKind::EDGE_KW, SyntaxKind::ELSE_KW, SyntaxKind::EMPTY_KW, SyntaxKind::ENABLE_KW, @@ -1489,6 +1521,8 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::GENERATED_KW, SyntaxKind::GLOBAL_KW, SyntaxKind::GRANTED_KW, + SyntaxKind::GRAPH_KW, + SyntaxKind::GRAPH_TABLE_KW, SyntaxKind::GREATEST_KW, SyntaxKind::GROUPING_KW, SyntaxKind::GROUPS_KW, @@ -1585,6 +1619,7 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::NFKC_KW, SyntaxKind::NFKD_KW, SyntaxKind::NO_KW, + SyntaxKind::NODE_KW, SyntaxKind::NONE_KW, SyntaxKind::NORMALIZE_KW, SyntaxKind::NORMALIZED_KW, @@ -1642,6 +1677,8 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::PROCEDURE_KW, SyntaxKind::PROCEDURES_KW, SyntaxKind::PROGRAM_KW, + SyntaxKind::PROPERTIES_KW, + SyntaxKind::PROPERTY_KW, SyntaxKind::PUBLICATION_KW, SyntaxKind::QUOTE_KW, SyntaxKind::QUOTES_KW, @@ -1655,9 +1692,11 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::REFERENCING_KW, SyntaxKind::REFRESH_KW, SyntaxKind::REINDEX_KW, + SyntaxKind::RELATIONSHIP_KW, SyntaxKind::RELATIVE_KW, SyntaxKind::RELEASE_KW, SyntaxKind::RENAME_KW, + SyntaxKind::REPACK_KW, SyntaxKind::REPEATABLE_KW, SyntaxKind::REPLACE_KW, SyntaxKind::REPLICA_KW, @@ -1770,6 +1809,7 @@ pub(crate) const BARE_LABEL_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::VARIADIC_KW, SyntaxKind::VERBOSE_KW, SyntaxKind::VERSION_KW, + SyntaxKind::VERTEX_KW, SyntaxKind::VIEW_KW, SyntaxKind::VIEWS_KW, SyntaxKind::VIRTUAL_KW, @@ -1867,6 +1907,7 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DELIMITERS_KW, SyntaxKind::DEPENDS_KW, SyntaxKind::DEPTH_KW, + SyntaxKind::DESTINATION_KW, SyntaxKind::DETACH_KW, SyntaxKind::DICTIONARY_KW, SyntaxKind::DISABLE_KW, @@ -1876,6 +1917,7 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::DOUBLE_KW, SyntaxKind::DROP_KW, SyntaxKind::EACH_KW, + SyntaxKind::EDGE_KW, SyntaxKind::EMPTY_KW, SyntaxKind::ENABLE_KW, SyntaxKind::ENCODING_KW, @@ -1906,6 +1948,7 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::GENERATED_KW, SyntaxKind::GLOBAL_KW, SyntaxKind::GRANTED_KW, + SyntaxKind::GRAPH_KW, SyntaxKind::GROUPS_KW, SyntaxKind::HANDLER_KW, SyntaxKind::HEADER_KW, @@ -1972,6 +2015,7 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::NFKC_KW, SyntaxKind::NFKD_KW, SyntaxKind::NO_KW, + SyntaxKind::NODE_KW, SyntaxKind::NORMALIZED_KW, SyntaxKind::NOTHING_KW, SyntaxKind::NOTIFY_KW, @@ -2016,6 +2060,8 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::PROCEDURE_KW, SyntaxKind::PROCEDURES_KW, SyntaxKind::PROGRAM_KW, + SyntaxKind::PROPERTIES_KW, + SyntaxKind::PROPERTY_KW, SyntaxKind::PUBLICATION_KW, SyntaxKind::QUOTE_KW, SyntaxKind::QUOTES_KW, @@ -2027,9 +2073,11 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::REFERENCING_KW, SyntaxKind::REFRESH_KW, SyntaxKind::REINDEX_KW, + SyntaxKind::RELATIONSHIP_KW, SyntaxKind::RELATIVE_KW, SyntaxKind::RELEASE_KW, SyntaxKind::RENAME_KW, + SyntaxKind::REPACK_KW, SyntaxKind::REPEATABLE_KW, SyntaxKind::REPLACE_KW, SyntaxKind::REPLICA_KW, @@ -2118,6 +2166,7 @@ pub(crate) const UNRESERVED_KEYWORDS: TokenSet = TokenSet::new(&[ SyntaxKind::VALUE_KW, SyntaxKind::VARYING_KW, SyntaxKind::VERSION_KW, + SyntaxKind::VERTEX_KW, SyntaxKind::VIEW_KW, SyntaxKind::VIEWS_KW, SyntaxKind::VIRTUAL_KW, diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index 2b67f55d..182c6c8e 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -985,7 +985,7 @@ fn xmlelement_fn(p: &mut Parser<'_>) -> CompletedMarker { if p.eat(XMLATTRIBUTES_KW) { // TODO: use delimited p.expect(L_PAREN); - xml_attribute_list(p); + expr_as_name_list(p); p.expect(R_PAREN); if p.eat(COMMA) && !opt_expr_list(p) { p.error("expected expression list"); @@ -1000,7 +1000,7 @@ fn xmlelement_fn(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, CALL_EXPR) } -fn xml_attribute_list(p: &mut Parser<'_>) { +fn expr_as_name_list(p: &mut Parser<'_>) { let m = p.start(); // TODO: use delimited while !p.at(EOF) && !p.at(R_PAREN) { @@ -1016,7 +1016,7 @@ fn xml_attribute_list(p: &mut Parser<'_>) { break; } } - m.complete(p, XML_ATTRIBUTE_LIST); + m.complete(p, EXPR_AS_NAME_LIST); } // XMLFOREST '(' xml_attribute_list ')' @@ -1025,7 +1025,7 @@ fn xmlforest_fn(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.expect(XMLFOREST_KW); p.expect(L_PAREN); - xml_attribute_list(p); + expr_as_name_list(p); p.expect(R_PAREN); let m = m.complete(p, XML_FOREST_FN).precede(p); opt_agg_clauses(p); @@ -1687,7 +1687,7 @@ fn path_segment(p: &mut Parser<'_>, kind: SyntaxKind) { m.complete(p, PATH_SEGMENT); } -const PATH_FIRST: TokenSet = COL_LABEL_FIRST; +const PATH_FIRST: TokenSet = NON_RESERVED_WORD; fn opt_path(p: &mut Parser<'_>, kind: SyntaxKind) -> Option { if !p.at_ts(PATH_FIRST) { @@ -3115,11 +3115,12 @@ const FROM_ITEM_KEYWORDS_FIRST: TokenSet = TokenSet::new(&[]) .union(FUNC_EXPR_COMMON_SUBEXPR_FIRST); const FROM_ITEM_FIRST: TokenSet = TokenSet::new(&[ - ONLY_KW, // optional - IDENT, // table_name, with_query_name, function_name - L_PAREN, // nested select stmt - LATERAL_KW, // optional - ROWS_KW, // rows from + ONLY_KW, // optional + IDENT, // table_name, with_query_name, function_name + L_PAREN, // nested select stmt + LATERAL_KW, // optional + ROWS_KW, // rows from + GRAPH_TABLE_KW, // GRAPH_TABLE(...) ]) .union(FROM_ITEM_KEYWORDS_FIRST); @@ -3167,6 +3168,10 @@ fn data_source(p: &mut Parser<'_>) { json_table_fn(p); opt_alias(p); } + GRAPH_TABLE_KW => { + graph_table_fn(p); + opt_alias(p); + } XMLTABLE_KW => { xml_table_fn(p); opt_alias(p); @@ -4090,7 +4095,7 @@ fn using_index(p: &mut Parser<'_>) { let m = p.start(); p.bump(USING_KW); p.expect(INDEX_KW); - name_ref(p); + opt_name_ref(p); m.complete(p, USING_INDEX); } @@ -5667,6 +5672,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { (ALTER_KW, OPERATOR_KW) if p.nth_at(2, FAMILY_KW) => Some(alter_operator_family(p)), (ALTER_KW, OPERATOR_KW) => Some(alter_operator(p)), (ALTER_KW, POLICY_KW) => Some(alter_policy(p)), + (ALTER_KW, PROPERTY_KW) => Some(alter_property_graph(p)), (ALTER_KW, PROCEDURAL_KW | LANGUAGE_KW) => Some(alter_language(p)), (ALTER_KW, PROCEDURE_KW) => Some(alter_procedure(p)), (ALTER_KW, PUBLICATION_KW) => Some(alter_publication(p)), @@ -5746,6 +5752,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { _ => Some(create_function(p)), } } + (CREATE_KW, PROPERTY_KW) => Some(create_property_graph(p)), (CREATE_KW, POLICY_KW) => Some(create_policy(p)), (CREATE_KW, PROCEDURE_KW) => Some(create_procedure(p)), (CREATE_KW, PUBLICATION_KW) => Some(create_publication(p)), @@ -5757,6 +5764,10 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { (CREATE_KW, SERVER_KW) => Some(create_server(p)), (CREATE_KW, STATISTICS_KW) => Some(create_statistics(p)), (CREATE_KW, SUBSCRIPTION_KW) => Some(create_subscription(p)), + (CREATE_KW, UNLOGGED_KW) if p.nth_at(2, PROPERTY_KW) => Some(create_property_graph(p)), + (CREATE_KW, LOCAL_KW | GLOBAL_KW) if p.nth_at(3, PROPERTY_KW) => { + Some(create_property_graph(p)) + } (CREATE_KW, TABLE_KW | GLOBAL_KW | LOCAL_KW | UNLOGGED_KW) if !p.nth_at(2, SEQUENCE_KW) => { Some(create_table(p)) } @@ -5768,6 +5779,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { match p.nth(2) { RECURSIVE_KW | VIEW_KW => Some(create_view(p)), SEQUENCE_KW => Some(create_sequence(p)), + PROPERTY_KW => Some(create_property_graph(p)), _ => Some(create_table(p)), } } @@ -5820,6 +5832,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { _ => Some(drop_operator(p)), }, (DROP_KW, OWNED_KW) => Some(drop_owned(p)), + (DROP_KW, PROPERTY_KW) => Some(drop_property_graph(p)), (DROP_KW, POLICY_KW) => Some(drop_policy(p)), (DROP_KW, PROCEDURE_KW) => Some(drop_procedure(p)), (DROP_KW, PUBLICATION_KW) => Some(drop_publication(p)), @@ -5873,6 +5886,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { (NOTIFY_KW, _) => Some(notify(p)), (PREPARE_KW, TRANSACTION_KW) => Some(prepare_transaction(p)), (PREPARE_KW, _) => Some(prepare(p)), + (REPACK_KW, _) => Some(repack(p)), (REASSIGN_KW, _) => Some(reassign(p)), (REFRESH_KW, _) => Some(refresh(p)), (REINDEX_KW, _) => Some(reindex(p)), @@ -8367,6 +8381,11 @@ fn comment(p: &mut Parser<'_>) -> CompletedMarker { p.expect(ON_KW); path_name_ref(p); } + PROPERTY_KW => { + p.bump_any(); + p.expect(GRAPH_KW); + path_name_ref(p); + } PROCEDURAL_KW => { p.bump_any(); p.expect(LANGUAGE_KW); @@ -8419,6 +8438,519 @@ fn cluster(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, CLUSTER) } +fn repack(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(REPACK_KW)); + let m = p.start(); + p.bump(REPACK_KW); + opt_option_list(p); + opt_table_and_columns_list(p); + if p.at(USING_KW) { + using_index(p); + } + m.complete(p, REPACK) +} + +fn create_property_graph(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(CREATE_KW)); + let m = p.start(); + p.bump(CREATE_KW); + opt_temp_or_unlogged(p); + p.expect(PROPERTY_KW); + p.expect(GRAPH_KW); + path_name(p); + opt_vertex_tables(p); + opt_edge_tables(p); + m.complete(p, CREATE_PROPERTY_GRAPH) +} + +fn opt_vertex_tables(p: &mut Parser<'_>) -> bool { + if !p.at(VERTEX_KW) && !p.at(NODE_KW) { + return false; + } + let m = p.start(); + p.bump_any(); // VERTEX or NODE + p.expect(TABLES_KW); + delimited( + p, + L_PAREN, + R_PAREN, + COMMA, + || "unexpected comma".to_string(), + VERTEX_TABLE_DEF_FIRST, + opt_vertex_table_def, + ); + m.complete(p, VERTEX_TABLES); + true +} + +const VERTEX_TABLE_DEF_FIRST: TokenSet = PATH_FIRST; + +fn opt_vertex_table_def(p: &mut Parser<'_>) -> bool { + if !p.at_ts(PATH_FIRST) { + return false; + } + let m = p.start(); + path_name_ref(p); + if p.eat(AS_KW) { + name(p); + } + opt_key_columns(p); + opt_element_table_label_and_properties(p); + m.complete(p, VERTEX_TABLE_DEF); + true +} + +fn opt_key_columns(p: &mut Parser<'_>) { + if p.eat(KEY_KW) { + opt_column_list_with(p, ColumnDefKind::Name); + } +} + +fn opt_element_table_label_and_properties(p: &mut Parser<'_>) { + if !opt_element_table_properties_clause(p) && p.at(DEFAULT_KW) || p.at(LABEL_KW) { + label_and_properties_list(p); + } +} + +fn label_and_properties_list(p: &mut Parser<'_>) { + let m = p.start(); + label_and_properties(p); + while p.at(DEFAULT_KW) || p.at(LABEL_KW) { + label_and_properties(p); + } + m.complete(p, LABEL_AND_PROPERTIES_LIST); +} + +fn label_and_properties(p: &mut Parser<'_>) { + let m = p.start(); + if p.eat(DEFAULT_KW) { + p.expect(LABEL_KW); + } else { + p.expect(LABEL_KW); + name(p); + } + opt_element_table_properties_clause(p); + m.complete(p, LABEL_AND_PROPERTIES); +} + +fn opt_element_table_properties_clause(p: &mut Parser<'_>) -> bool { + if !p.at(NO_KW) && !p.at(PROPERTIES_KW) { + return false; + } + let m = p.start(); + let kind = if p.eat(NO_KW) { + p.expect(PROPERTIES_KW); + NO_PROPERTIES + } else { + p.expect(PROPERTIES_KW); + if p.eat(ALL_KW) { + p.expect(COLUMNS_KW); + ALL_PROPERTIES + } else { + p.expect(L_PAREN); + expr_as_name_list(p); + p.expect(R_PAREN); + PROPERTIES_LIST + } + }; + m.complete(p, kind); + true +} + +fn opt_edge_tables(p: &mut Parser<'_>) { + if !p.at(EDGE_KW) && !p.at(RELATIONSHIP_KW) { + return; + } + let m = p.start(); + p.bump_any(); + p.expect(TABLES_KW); + delimited( + p, + L_PAREN, + R_PAREN, + COMMA, + || "unexpected comma".to_string(), + EDGE_TABLE_DEF_FIRST, + opt_edge_table_def, + ); + m.complete(p, EDGE_TABLES); +} + +const EDGE_TABLE_DEF_FIRST: TokenSet = PATH_FIRST; + +// foo AS bar KEY (a, b) +// SOURCE KEY (a, b) REFERENCES t (c, d) +// DESTINATION KEY (a, b) REFERENCES t (c, d) +fn opt_edge_table_def(p: &mut Parser<'_>) -> bool { + if !p.at_ts(EDGE_TABLE_DEF_FIRST) { + return false; + } + let m = p.start(); + path_name_ref(p); + if p.eat(AS_KW) { + name(p); + } + opt_key_columns(p); + source_vertex_table(p); + dest_vertex_table(p); + opt_element_table_label_and_properties(p); + m.complete(p, EDGE_TABLE_DEF); + true +} + +// SOURCE KEY (a, b) REFERENCES t (c, d) +// SOURCE t +fn source_vertex_table(p: &mut Parser<'_>) { + let m = p.start(); + p.expect(SOURCE_KW); + if p.eat(KEY_KW) { + opt_column_list_with(p, ColumnDefKind::Name); + p.expect(REFERENCES_KW); + name_ref(p); + opt_column_list_with(p, ColumnDefKind::Name); + } else { + name(p); + } + m.complete(p, SOURCE_VERTEX_TABLE); +} + +// DESTINATION KEY (a, b) REFERENCES t (c, d) +// DESTINATION t +fn dest_vertex_table(p: &mut Parser<'_>) { + let m = p.start(); + p.expect(DESTINATION_KW); + if p.eat(KEY_KW) { + opt_column_list_with(p, ColumnDefKind::Name); + p.expect(REFERENCES_KW); + name_ref(p); + opt_column_list_with(p, ColumnDefKind::Name); + } else { + name(p); + } + m.complete(p, DEST_VERTEX_TABLE); +} + +fn alter_property_graph(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(ALTER_KW)); + let m = p.start(); + p.bump(ALTER_KW); + p.expect(PROPERTY_KW); + p.expect(GRAPH_KW); + opt_if_exists(p); + path_name_ref(p); + alter_property_graph_action(p); + m.complete(p, ALTER_PROPERTY_GRAPH) +} + +const VERTEX: TokenSet = TokenSet::new(&[VERTEX_KW, NODE_KW]); +const EDGE: TokenSet = TokenSet::new(&[EDGE_KW, RELATIONSHIP_KW]); +const VERTEX_OR_EDGE: TokenSet = VERTEX.union(EDGE); + +fn alter_property_graph_action(p: &mut Parser<'_>) { + if p.at(RENAME_KW) { + rename_to(p); + } else if p.at(OWNER_KW) { + owner_to(p); + } else if p.at(SET_KW) { + set_schema(p); + } else if p.at(ADD_KW) { + add_vertex_edge_tables(p); + } else if p.at(DROP_KW) && p.nth_at_ts(1, VERTEX) { + drop_vertex_tables(p); + } else if p.at(DROP_KW) && p.nth_at_ts(1, EDGE) { + drop_edge_tables(p); + } else if p.at(ALTER_KW) && p.nth_at_ts(1, VERTEX_OR_EDGE) { + alter_vertex_edge_table(p); + } else { + p.error("expected alter property graph action"); + } +} + +fn alter_vertex_edge_table(p: &mut Parser<'_>) { + assert!(p.at(ALTER_KW) && p.nth_at_ts(1, VERTEX_OR_EDGE)); + let m = p.start(); + p.bump(ALTER_KW); + p.bump_any(); + p.expect(TABLE_KW); + path_name_ref(p); + let kind = alter_element_table_actions(p); + m.complete(p, kind); +} + +fn drop_edge_tables(p: &mut Parser<'_>) { + let m = p.start(); + p.bump(DROP_KW); + p.bump_any(); // EDGE/RELATIONSHIP + p.expect(TABLES_KW); + p.expect(L_PAREN); + path_name_ref(p); + while p.eat(COMMA) { + path_name_ref(p); + } + p.expect(R_PAREN); + opt_cascade_or_restrict(p); + m.complete(p, DROP_EDGE_TABLES); +} + +fn drop_vertex_tables(p: &mut Parser<'_>) { + assert!(p.at(DROP_KW)); + let m = p.start(); + p.bump(DROP_KW); + p.bump_any(); // VERTEX/NODE + p.expect(TABLES_KW); + p.expect(L_PAREN); + path_name_ref(p); + while p.eat(COMMA) { + path_name_ref(p); + } + p.expect(R_PAREN); + opt_cascade_or_restrict(p); + m.complete(p, DROP_VERTEX_TABLES); +} + +fn add_vertex_edge_tables(p: &mut Parser<'_>) { + assert!(p.at(ADD_KW)); + let m = p.start(); + p.bump(ADD_KW); + let is_vertex = opt_vertex_tables(p); + if p.eat(ADD_KW) || !is_vertex { + opt_edge_tables(p); + } + m.complete(p, ADD_VERTEX_EDGE_TABLES); +} + +fn alter_element_table_actions(p: &mut Parser<'_>) -> SyntaxKind { + if p.at(ADD_KW) { + add_label(p); + while p.at(ADD_KW) { + add_label(p); + } + ALTER_VERTEX_EDGE_LABELS + } else if p.at(DROP_KW) && (p.nth_at(1, LABEL_KW) || p.nth_at(1, PROPERTIES_KW)) { + p.bump(DROP_KW); + if p.eat(LABEL_KW) { + name_ref(p); + DROP_VERTEX_EDGE_LABEL + } else { + p.expect(PROPERTIES_KW); + if !opt_paren_name_ref_list(p) { + p.error("expected name ref list") + } + DROP_VERTEX_EDGE_LABEL_PROPERTIES + } + } else { + p.expect(ALTER_KW); + p.expect(LABEL_KW); + name_ref(p); + if p.eat(ADD_KW) { + p.expect(PROPERTIES_KW); + p.expect(L_PAREN); + expr_as_name_list(p); + p.expect(R_PAREN); + ADD_VERTEX_EDGE_LABEL_PROPERTIES + } else { + p.expect(DROP_KW); + p.expect(PROPERTIES_KW); + if !opt_paren_name_ref_list(p) { + p.error("expected name ref list") + } + opt_cascade_or_restrict(p); + DROP_VERTEX_EDGE_LABEL_PROPERTIES + } + } +} + +fn add_label(p: &mut Parser<'_>) { + assert!(p.at(ADD_KW)); + let m = p.start(); + p.bump(ADD_KW); + p.expect(LABEL_KW); + name(p); + opt_element_table_properties_clause(p); + m.complete(p, ADD_LABEL); +} + +fn drop_property_graph(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(DROP_KW)); + let m = p.start(); + p.bump(DROP_KW); + p.expect(PROPERTY_KW); + p.expect(GRAPH_KW); + opt_if_exists(p); + path_name_ref(p); + opt_cascade_or_restrict(p); + m.complete(p, DROP_PROPERTY_GRAPH) +} + +fn graph_table_fn(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(GRAPH_TABLE_KW)); + let m = p.start(); + p.bump(GRAPH_TABLE_KW); + p.expect(L_PAREN); + path_name_ref(p); + p.expect(MATCH_KW); + path_pattern_list(p); + opt_where_clause(p); + p.expect(COLUMNS_KW); + p.expect(L_PAREN); + expr_as_name_list(p); + p.expect(R_PAREN); + p.expect(R_PAREN); + m.complete(p, GRAPH_TABLE_FN) +} + +fn path_pattern_list(p: &mut Parser<'_>) { + let m = p.start(); + if p.at(L_PAREN) || p.at(MINUS) || p.at(L_ANGLE) { + path_pattern(p); + while p.eat(COMMA) { + path_pattern(p); + } + } + m.complete(p, PATH_PATTERN_LIST); +} + +fn path_pattern(p: &mut Parser<'_>) { + let m = p.start(); + path_factor(p); + while p.at_ts(PATH_FACTOR_FIRST) { + path_factor(p); + } + m.complete(p, PATH_PATTERN); +} + +const PATH_FACTOR_FIRST: TokenSet = TokenSet::new(&[L_PAREN, MINUS, L_ANGLE]); + +fn path_factor(p: &mut Parser<'_>) { + let m = p.start(); + path_primary(p); + opt_graph_pattern_qualifier(p); + m.complete(p, PATH_FACTOR); +} + +fn path_primary(p: &mut Parser<'_>) { + if p.at(L_PAREN) { + if p.nth_at_ts(1, PATH_FACTOR_FIRST) { + paren_graph_pattern(p); + } else { + vertex_pattern(p); + } + } else if p.at(L_ANGLE) { + // <- + // <-[...]- + edge_left(p); + } else if p.at(MINUS) { + if p.nth_at(1, L_BRACK) { + // -[...]- or -[...]-> + edge_with_bracket(p); + } else if p.nth_at(1, R_ANGLE) { + // -> + let m = p.start(); + p.bump(MINUS); + p.bump(R_ANGLE); + m.complete(p, EDGE_RIGHT); + } else { + // - + let m = p.start(); + p.bump(MINUS); + m.complete(p, EDGE_ANY); + } + } +} + +fn vertex_pattern(p: &mut Parser<'_>) { + let m = p.start(); + p.expect(L_PAREN); + opt_name(p); + opt_is_label_expression(p); + opt_where_clause(p); + p.expect(R_PAREN); + m.complete(p, VERTEX_PATTERN); +} + +fn opt_is_label_expression(p: &mut Parser<'_>) { + if p.at(IS_KW) { + let m = p.start(); + p.bump(IS_KW); + if expr(p).is_none() { + p.error("expected expression"); + } + m.complete(p, IS_LABEL_EXPRESSION); + } +} + +// <- +// <-[ a is foo where c > 10 ]- +fn edge_left(p: &mut Parser<'_>) { + assert!(p.at(L_ANGLE)); + let m = p.start(); + p.bump(L_ANGLE); + p.expect(MINUS); + if p.at(L_BRACK) { + p.bump(L_BRACK); + opt_edge_pattern_inner(p); + p.expect(R_BRACK); + p.expect(MINUS); + } + m.complete(p, EDGE_LEFT); +} + +// -[ ... ]-> +// -[ ... ]- +fn edge_with_bracket(p: &mut Parser<'_>) { + assert!(p.at(MINUS) && p.nth_at(1, L_BRACK)); + let m = p.start(); + p.bump(MINUS); + p.bump(L_BRACK); + opt_edge_pattern_inner(p); + p.expect(R_BRACK); + p.expect(MINUS); + let kind = if p.eat(R_ANGLE) { EDGE_RIGHT } else { EDGE_ANY }; + m.complete(p, kind); +} + +fn opt_edge_pattern_inner(p: &mut Parser<'_>) { + opt_name(p); + opt_is_label_expression(p); + opt_where_clause(p); +} + +fn paren_graph_pattern(p: &mut Parser<'_>) { + let m = p.start(); + p.expect(L_PAREN); + path_pattern(p); + opt_where_clause(p); + p.expect(R_PAREN); + m.complete(p, PAREN_GRAPH_PATTERN); +} + +// { Iconst } +// { , Iconst } +// { Iconst , Iconst } +fn opt_graph_pattern_qualifier(p: &mut Parser<'_>) { + if !p.at(L_CURLY) { + return; + } + let m = p.start(); + p.expect(L_CURLY); + if p.eat(COMMA) { + if expr(p).is_none() { + p.error("expected expression"); + } + } else { + if expr(p).is_none() { + p.error("expected expression"); + } + if p.eat(COMMA) { + if expr(p).is_none() { + p.error("expected expression"); + } + } + } + p.expect(R_CURLY); + m.complete(p, GRAPH_PATTERN_QUALIFIER); +} + const OPTION_FIRST: TokenSet = TokenSet::new(&[ANALYSE_KW, ANALYZE_KW, FORMAT_KW]).union(NON_RESERVED_WORD); @@ -10845,6 +11377,11 @@ fn privilege_target(p: &mut Parser<'_>) { } } } + PROPERTY_KW => { + p.bump(PROPERTY_KW); + p.expect(GRAPH_KW); + path_name_ref_list(p); + } // table_name [, ...] _ if p.at_ts(COL_LABEL_FIRST) => { path_name_ref_list(p); diff --git a/crates/squawk_parser/src/lexed_str.rs b/crates/squawk_parser/src/lexed_str.rs index 411b5c9c..9b1e25e2 100644 --- a/crates/squawk_parser/src/lexed_str.rs +++ b/crates/squawk_parser/src/lexed_str.rs @@ -193,6 +193,8 @@ impl<'a> Converter<'a> { squawk_lexer::TokenKind::CloseParen => SyntaxKind::R_PAREN, squawk_lexer::TokenKind::OpenBracket => SyntaxKind::L_BRACK, squawk_lexer::TokenKind::CloseBracket => SyntaxKind::R_BRACK, + squawk_lexer::TokenKind::OpenCurly => SyntaxKind::L_CURLY, + squawk_lexer::TokenKind::CloseCurly => SyntaxKind::R_CURLY, squawk_lexer::TokenKind::At => SyntaxKind::AT, squawk_lexer::TokenKind::Pound => SyntaxKind::POUND, squawk_lexer::TokenKind::Tilde => SyntaxKind::TILDE, diff --git a/crates/squawk_parser/tests/data/ok/alter_property_graph.sql b/crates/squawk_parser/tests/data/ok/alter_property_graph.sql new file mode 100644 index 00000000..cf0a266a --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/alter_property_graph.sql @@ -0,0 +1,38 @@ +-- rename to +alter property graph if exists foo.bar + rename to r; + +-- set owner +alter property graph if exists foo.bar + owner to o; + +-- set schema +alter property graph if exists foo.bar + set schema s; + +-- add vertex/edge tables +alter property graph if exists foo.bar + add vertex tables ( + a key (c, k), + d key (l, u) + no properties + ) + add edge tables ( + a key (x, y) + source key (s) references k (id) + destination key (d) references k (id) + label q properties (o, f * 10 as p) + label q properties (i as x) + ); + +-- add vertex/edge tables part 2 +alter property graph if exists g + add vertex tables ( + d key (l) + properties all columns + ) + add edge tables ( + a key (x, y) + source key (s) references k (id) + destination key (d) references k (id) + ); diff --git a/crates/squawk_parser/tests/data/ok/create_property_graph.sql b/crates/squawk_parser/tests/data/ok/create_property_graph.sql new file mode 100644 index 00000000..1e70d84e --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/create_property_graph.sql @@ -0,0 +1,67 @@ +-- all the temps +create temporary property graph f; +create temp property graph f; +create local temporary property graph f; +create local temp property graph f; +create global temporary property graph f; +create global temp property graph f; +create unlogged property graph f; + +create temp property graph foo.bar; + +-- vertex & table edges +create property graph foo.bar + vertex tables (buzz.boo as foo key (a, b) no properties) + edge tables (foo.bar as z key (x, y) + source key (a, b) references k (t, y) + destination key (q, t) references a (r, j) + properties all columns); + +create property graph g + edge tables (e + source s + destination d + no properties); + +create property graph g + edge tables (e + source s + destination d + properties (a as k, f as y)); + +-- vertex & table edges abbr +create property graph foo.bar + vertex tables (boo) + edge tables ( + bar + source z + destination y + ); + +-- vertex tables only +create property graph foo + edge tables ( + e + source z + destination y + ); + +-- edge tables only +create property graph bar + edge tables ( + e + source z + destination y + ); + +-- vertex & table multiple tables +create property graph foo.bar + vertex tables (boo, buzz) + edge tables ( + bar + source z + destination y, + foo + source z + destination y + ); diff --git a/crates/squawk_parser/tests/data/ok/drop_property_graph.sql b/crates/squawk_parser/tests/data/ok/drop_property_graph.sql new file mode 100644 index 00000000..93a17b71 --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/drop_property_graph.sql @@ -0,0 +1,5 @@ +drop property graph g; + +drop property graph if exists foo.bar cascade; + +drop property graph g restrict; diff --git a/crates/squawk_parser/tests/data/ok/repack.sql b/crates/squawk_parser/tests/data/ok/repack.sql new file mode 100644 index 00000000..04b14d09 --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/repack.sql @@ -0,0 +1,9 @@ +repack; + +repack using index; + +repack using index idx; + +-- full +repack (verbose false) foo.bar (a, b) using index idx; + diff --git a/crates/squawk_parser/tests/data/ok/select_graph_table_fn.sql b/crates/squawk_parser/tests/data/ok/select_graph_table_fn.sql new file mode 100644 index 00000000..4e278823 --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/select_graph_table_fn.sql @@ -0,0 +1,153 @@ + +-- simple +select * from graph_table( + foo match - columns (a) +); + +-- edge left +select * from graph_table( + foo match <-[ foo is bar where x > 10 ]- + where x > 10 and z = 4 + columns (a) +); +select * from graph_table( + foo match <- columns (a) +); + +-- edge right +select * from graph_table( + foo match -[ foo is bar where x > 10 ]-> + where x > 10 and z = 4 + columns (a) +); +select * from graph_table( + foo match -> columns (a) +); + +-- edge any +select * from graph_table( + foo match -[ foo is bar where x > 10 ]- + where 1=1 + columns (a) +); +select * from graph_table( + foo match - columns (a) +); + +-- paren graph pattern +select * from graph_table( + foo match (o is foo where a > 10) + columns (a) +); + +select * from graph_table( + foo match (((o is foo where a > 10))) + columns (a) +); + +select * from graph_table( + foo match (<-) + columns (a) +); + +-- graph pattern qualifier +select * from graph_table( + foo match <- {100} columns (a) +); + +select * from graph_table( + foo match - {1,2} columns (a) +); + +select * from graph_table( + foo match - {,2} columns (a) +); + +-- multiple path factors +select * from graph_table( + foo match -[ foo is bar where x > 10 ]- + columns (a) +); + +select * from graph_table( + t + match (c is customers)-[co is customer_orders]->(o is orders where o.ordered_when = date '2024-01-02') + columns (c.name, c.address) +); + +-- complicated + +select * from graph_table( + myshop match + (a is customers where a.id = 1) + -[e1 is customer_orders]-> + (b is orders where b.ordered_when > date '2024-01-01') + -[e2 is order_items]- + (c is products where c.price > 10) + <-[e3 is wishlist_items]- + (d is wishlists) + columns (a.name, b.order_id, c.name, d.wishlist_name) +); + +select * from graph_table( + myshop match + (a is customers)-[is customer_orders]->(b is orders), + (b is orders)-[is order_items]->(c is products), + (a is customers)-[is customer_wishlists]->(w is wishlists) + columns (a.name, b.order_id, c.name, w.wishlist_name) +); + +select * from graph_table( + myshop match + (a is customers where a.address = 'US') + ->( + (b is orders where b.ordered_when = date '2024-06-01') + -[e is order_items]-> + (c is products where c.price > 100) + where b.order_id > 0 + ) + columns (a.name, c.name) +); + +select * from graph_table( + myshop match + (a is customers) + -{1,3} + (b is orders) + -[is order_items]- + (c is products) + (<-[is wishlist_items]-(d is wishlists)) + -> + (e is customers where e.name = 'Alice') + columns (a.name, b.order_id, c.name, d.wishlist_name, e.name) +); + +select * from graph_table( + myshop match + ((a is customers) + -[e1 is customer_orders]-> + ((b is orders) + -[e2 is order_items]-> + (c is products) + where c.price > 50 + ) + where b.ordered_when > date '2023-01-01' + ) + columns (a.name, c.name) +); + +select * from graph_table( + myshop match + (a is customers) + -> + (b is orders) + <- + (c is wishlists) + - + (d is products) + -[e is order_items]-> + (f is orders) + <-[g is customer_orders]- + (h is customers where h.id <> a.id) + columns (a.name, d.name, h.name) +); diff --git a/crates/squawk_parser/tests/snapshots/tests__alter_property_graph_ok.snap b/crates/squawk_parser/tests/snapshots/tests__alter_property_graph_ok.snap new file mode 100644 index 00000000..67eae12e --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__alter_property_graph_ok.snap @@ -0,0 +1,454 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/alter_property_graph.sql +--- +SOURCE_FILE + COMMENT "-- rename to" + WHITESPACE "\n" + ALTER_PROPERTY_GRAPH + ALTER_KW "alter" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + RENAME_TO + RENAME_KW "rename" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + NAME + IDENT "r" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- set owner" + WHITESPACE "\n" + ALTER_PROPERTY_GRAPH + ALTER_KW "alter" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + OWNER_TO + OWNER_KW "owner" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + ROLE_REF + NAME_REF + IDENT "o" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- set schema" + WHITESPACE "\n" + ALTER_PROPERTY_GRAPH + ALTER_KW "alter" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + SET_SCHEMA + SET_KW "set" + WHITESPACE " " + SCHEMA_KW "schema" + WHITESPACE " " + NAME_REF + IDENT "s" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- add vertex/edge tables" + WHITESPACE "\n" + ALTER_PROPERTY_GRAPH + ALTER_KW "alter" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + ADD_VERTEX_EDGE_TABLES + ADD_KW "add" + WHITESPACE " " + VERTEX_TABLES + VERTEX_KW "vertex" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "c" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "k" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "l" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "u" + R_PAREN ")" + WHITESPACE " \n " + NO_PROPERTIES + NO_KW "no" + WHITESPACE " " + PROPERTIES_KW "properties" + WHITESPACE "\n " + R_PAREN ")" + WHITESPACE "\n " + ADD_KW "add" + WHITESPACE " " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "x" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "y" + R_PAREN ")" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "s" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "k" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "id" + R_PAREN ")" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "d" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "k" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "id" + R_PAREN ")" + WHITESPACE "\n " + LABEL_AND_PROPERTIES_LIST + LABEL_AND_PROPERTIES + LABEL_KW "label" + WHITESPACE " " + NAME + IDENT "q" + WHITESPACE " " + PROPERTIES_LIST + PROPERTIES_KW "properties" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "o" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + BIN_EXPR + NAME_REF + IDENT "f" + WHITESPACE " " + STAR "*" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "p" + R_PAREN ")" + WHITESPACE "\n " + LABEL_AND_PROPERTIES + LABEL_KW "label" + WHITESPACE " " + NAME + IDENT "q" + WHITESPACE " " + PROPERTIES_LIST + PROPERTIES_KW "properties" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "i" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "x" + R_PAREN ")" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- add vertex/edge tables part 2" + WHITESPACE "\n" + ALTER_PROPERTY_GRAPH + ALTER_KW "alter" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "g" + WHITESPACE "\n " + ADD_VERTEX_EDGE_TABLES + ADD_KW "add" + WHITESPACE " " + VERTEX_TABLES + VERTEX_KW "vertex" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "l" + R_PAREN ")" + WHITESPACE " \n " + ALL_PROPERTIES + PROPERTIES_KW "properties" + WHITESPACE " " + ALL_KW "all" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE "\n " + R_PAREN ")" + WHITESPACE "\n " + ADD_KW "add" + WHITESPACE " " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "x" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "y" + R_PAREN ")" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "s" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "k" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "id" + R_PAREN ")" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "d" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "k" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "id" + R_PAREN ")" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__create_property_graph_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_property_graph_ok.snap new file mode 100644 index 00000000..892cdc83 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__create_property_graph_ok.snap @@ -0,0 +1,650 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/create_property_graph.sql +--- +SOURCE_FILE + COMMENT "-- all the temps" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + TEMPORARY_KW "temporary" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + TEMP_KW "temp" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + LOCAL_KW "local" + WHITESPACE " " + TEMPORARY_KW "temporary" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + LOCAL_KW "local" + WHITESPACE " " + TEMP_KW "temp" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + GLOBAL_KW "global" + WHITESPACE " " + TEMPORARY_KW "temporary" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + GLOBAL_KW "global" + WHITESPACE " " + TEMP_KW "temp" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + UNLOGGED_KW "unlogged" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + TEMP_KW "temp" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME + IDENT "bar" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- vertex & table edges" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME + IDENT "bar" + WHITESPACE "\n " + VERTEX_TABLES + VERTEX_KW "vertex" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + VERTEX_TABLE_DEF + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "buzz" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "boo" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "foo" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "a" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "b" + R_PAREN ")" + WHITESPACE " " + NO_PROPERTIES + NO_KW "no" + WHITESPACE " " + PROPERTIES_KW "properties" + R_PAREN ")" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + EDGE_TABLE_DEF + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "x" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "y" + R_PAREN ")" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "a" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "b" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "k" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "t" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "y" + R_PAREN ")" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "q" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "t" + R_PAREN ")" + WHITESPACE " " + REFERENCES_KW "references" + WHITESPACE " " + NAME_REF + IDENT "a" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "r" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "j" + R_PAREN ")" + WHITESPACE "\n " + ALL_PROPERTIES + PROPERTIES_KW "properties" + WHITESPACE " " + ALL_KW "all" + WHITESPACE " " + COLUMNS_KW "columns" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "g" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "s" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "d" + WHITESPACE "\n " + NO_PROPERTIES + NO_KW "no" + WHITESPACE " " + PROPERTIES_KW "properties" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "g" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "s" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "d" + WHITESPACE "\n " + PROPERTIES_LIST + PROPERTIES_KW "properties" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "k" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + NAME_REF + IDENT "f" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + NAME + IDENT "y" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- vertex & table edges abbr" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME + IDENT "bar" + WHITESPACE "\n " + VERTEX_TABLES + VERTEX_KW "vertex" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "boo" + R_PAREN ")" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "y" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- vertex tables only" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "foo" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "y" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- edge tables only" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "bar" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "y" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- vertex & table multiple tables" + WHITESPACE "\n" + CREATE_PROPERTY_GRAPH + CREATE_KW "create" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME + IDENT "bar" + WHITESPACE "\n " + VERTEX_TABLES + VERTEX_KW "vertex" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "boo" + COMMA "," + WHITESPACE " " + VERTEX_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "buzz" + R_PAREN ")" + WHITESPACE "\n " + EDGE_TABLES + EDGE_KW "edge" + WHITESPACE " " + TABLES_KW "tables" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "y" + COMMA "," + WHITESPACE "\n " + EDGE_TABLE_DEF + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE "\n " + SOURCE_VERTEX_TABLE + SOURCE_KW "source" + WHITESPACE " " + NAME + IDENT "z" + WHITESPACE "\n " + DEST_VERTEX_TABLE + DESTINATION_KW "destination" + WHITESPACE " " + NAME + IDENT "y" + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__drop_property_graph_ok.snap b/crates/squawk_parser/tests/snapshots/tests__drop_property_graph_ok.snap new file mode 100644 index 00000000..321aaefa --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__drop_property_graph_ok.snap @@ -0,0 +1,58 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/drop_property_graph.sql +--- +SOURCE_FILE + DROP_PROPERTY_GRAPH + DROP_KW "drop" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "g" + SEMICOLON ";" + WHITESPACE "\n\n" + DROP_PROPERTY_GRAPH + DROP_KW "drop" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + IF_EXISTS + IF_KW "if" + WHITESPACE " " + EXISTS_KW "exists" + WHITESPACE " " + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE " " + CASCADE_KW "cascade" + SEMICOLON ";" + WHITESPACE "\n\n" + DROP_PROPERTY_GRAPH + DROP_KW "drop" + WHITESPACE " " + PROPERTY_KW "property" + WHITESPACE " " + GRAPH_KW "graph" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "g" + WHITESPACE " " + RESTRICT_KW "restrict" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_create_property_graph.snap b/crates/squawk_parser/tests/snapshots/tests__regression_create_property_graph.snap new file mode 100644 index 00000000..1acf364f --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__regression_create_property_graph.snap @@ -0,0 +1,5 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: postgres/regression_suite/create_property_graph.sql +--- + diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_graph_table.snap b/crates/squawk_parser/tests/snapshots/tests__regression_graph_table.snap new file mode 100644 index 00000000..6bcd2759 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__regression_graph_table.snap @@ -0,0 +1,5 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: postgres/regression_suite/graph_table.sql +--- + diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_graph_table_rls.snap b/crates/squawk_parser/tests/snapshots/tests__regression_graph_table_rls.snap new file mode 100644 index 00000000..0cb8fa6d --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__regression_graph_table_rls.snap @@ -0,0 +1,5 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: postgres/regression_suite/graph_table_rls.sql +--- + diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_planner_est.snap b/crates/squawk_parser/tests/snapshots/tests__regression_planner_est.snap new file mode 100644 index 00000000..bce16edd --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__regression_planner_est.snap @@ -0,0 +1,5 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: postgres/regression_suite/planner_est.sql +--- + diff --git a/crates/squawk_parser/tests/snapshots/tests__repack_ok.snap b/crates/squawk_parser/tests/snapshots/tests__repack_ok.snap new file mode 100644 index 00000000..ac798781 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__repack_ok.snap @@ -0,0 +1,78 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/repack.sql +--- +SOURCE_FILE + REPACK + REPACK_KW "repack" + SEMICOLON ";" + WHITESPACE "\n\n" + REPACK + REPACK_KW "repack" + WHITESPACE " " + USING_INDEX + USING_KW "using" + WHITESPACE " " + INDEX_KW "index" + SEMICOLON ";" + WHITESPACE "\n\n" + REPACK + REPACK_KW "repack" + WHITESPACE " " + USING_INDEX + USING_KW "using" + WHITESPACE " " + INDEX_KW "index" + WHITESPACE " " + NAME_REF + IDENT "idx" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- full" + WHITESPACE "\n" + REPACK + REPACK_KW "repack" + WHITESPACE " " + OPTION_ITEM_LIST + L_PAREN "(" + OPTION_ITEM + VERBOSE_KW "verbose" + WHITESPACE " " + LITERAL + FALSE_KW "false" + R_PAREN ")" + WHITESPACE " " + TABLE_AND_COLUMNS_LIST + TABLE_AND_COLUMNS + RELATION_NAME + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + DOT "." + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME_REF + IDENT "a" + COMMA "," + WHITESPACE " " + COLUMN + NAME_REF + IDENT "b" + R_PAREN ")" + WHITESPACE " " + USING_INDEX + USING_KW "using" + WHITESPACE " " + INDEX_KW "index" + WHITESPACE " " + NAME_REF + IDENT "idx" + SEMICOLON ";" + WHITESPACE "\n\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap index dd32be30..f1a42ac6 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap @@ -834,7 +834,7 @@ SOURCE_FILE WHITESPACE " " FROM_ITEM NAME_REF - IDENT "graph" + GRAPH_KW "graph" WHITESPACE " " ALIAS NAME @@ -895,7 +895,7 @@ SOURCE_FILE WHITESPACE " " FROM_ITEM NAME_REF - IDENT "graph" + GRAPH_KW "graph" WHITESPACE " " ALIAS NAME diff --git a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap index f0e969a4..aeb71a4f 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap @@ -848,7 +848,7 @@ SOURCE_FILE WHITESPACE " " XMLATTRIBUTES_KW "xmlattributes" L_PAREN "(" - XML_ATTRIBUTE_LIST + EXPR_AS_NAME_LIST EXPR_AS_NAME NAME_REF IDENT "foo" @@ -913,7 +913,7 @@ SOURCE_FILE WHITESPACE " " XMLATTRIBUTES_KW "xmlattributes" L_PAREN "(" - XML_ATTRIBUTE_LIST + EXPR_AS_NAME_LIST EXPR_AS_NAME NAME_REF IDENT "buzz" @@ -1057,7 +1057,7 @@ SOURCE_FILE XML_FOREST_FN XMLFOREST_KW "xmlforest" L_PAREN "(" - XML_ATTRIBUTE_LIST + EXPR_AS_NAME_LIST EXPR_AS_NAME NAME_REF IDENT "foo" diff --git a/crates/squawk_parser/tests/snapshots/tests__select_graph_table_fn_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_graph_table_fn_ok.snap new file mode 100644 index 00000000..8d813008 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__select_graph_table_fn_ok.snap @@ -0,0 +1,2123 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/select_graph_table_fn.sql +--- +SOURCE_FILE + WHITESPACE "\n" + COMMENT "-- simple" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- edge left" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + L_BRACK "[" + WHITESPACE " " + NAME + IDENT "foo" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "bar" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + AND_KW "and" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "z" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "4" + WHITESPACE " \n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- edge right" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + WHITESPACE " " + NAME + IDENT "foo" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "bar" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + AND_KW "and" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "z" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "4" + WHITESPACE " \n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + R_ANGLE ">" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- edge any" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + L_BRACK "[" + WHITESPACE " " + NAME + IDENT "foo" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "bar" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + LITERAL + INT_NUMBER "1" + EQ "=" + LITERAL + INT_NUMBER "1" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- paren graph pattern" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "o" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "foo" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "a" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "o" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "foo" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "a" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + R_PAREN ")" + R_PAREN ")" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- graph pattern qualifier" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + WHITESPACE " " + GRAPH_PATTERN_QUALIFIER + L_CURLY "{" + LITERAL + INT_NUMBER "100" + R_CURLY "}" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + WHITESPACE " " + GRAPH_PATTERN_QUALIFIER + L_CURLY "{" + LITERAL + INT_NUMBER "1" + COMMA "," + LITERAL + INT_NUMBER "2" + R_CURLY "}" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + WHITESPACE " " + GRAPH_PATTERN_QUALIFIER + L_CURLY "{" + COMMA "," + LITERAL + INT_NUMBER "2" + R_CURLY "}" + WHITESPACE " " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- multiple path factors" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + EDGE_ANY + MINUS "-" + L_BRACK "[" + WHITESPACE " " + NAME + IDENT "foo" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "bar" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + NAME_REF + IDENT "x" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + WHITESPACE " " + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + NAME_REF + IDENT "a" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE "\n " + MATCH_KW "match" + WHITESPACE " " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "co" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_orders" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "o" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "o" + DOT "." + NAME_REF + IDENT "ordered_when" + WHITESPACE " " + EQ "=" + WHITESPACE " " + CAST_EXPR + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "date" + WHITESPACE " " + LITERAL + STRING "'2024-01-02'" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + IDENT "address" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- complicated" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + IDENT "id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "e1" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_orders" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "ordered_when" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + CAST_EXPR + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "date" + WHITESPACE " " + LITERAL + STRING "'2024-01-01'" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_ANY + MINUS "-" + L_BRACK "[" + NAME + IDENT "e2" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + IDENT "price" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + L_BRACK "[" + NAME + IDENT "e3" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlist_items" + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "d" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlists" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "order_id" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "d" + DOT "." + NAME_REF + IDENT "wishlist_name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_orders" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_wishlists" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "w" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlists" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "order_id" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "w" + DOT "." + NAME_REF + IDENT "wishlist_name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + IDENT "address" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "'US'" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + R_ANGLE ">" + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + WHITESPACE "\n " + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "ordered_when" + WHITESPACE " " + EQ "=" + WHITESPACE " " + CAST_EXPR + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "date" + WHITESPACE " " + LITERAL + STRING "'2024-06-01'" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "e" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + IDENT "price" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "100" + R_PAREN ")" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "order_id" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE "\n " + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_ANY + MINUS "-" + GRAPH_PATTERN_QUALIFIER + L_CURLY "{" + LITERAL + INT_NUMBER "1" + COMMA "," + LITERAL + INT_NUMBER "3" + R_CURLY "}" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_ANY + MINUS "-" + L_BRACK "[" + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + L_BRACK "[" + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlist_items" + R_BRACK "]" + MINUS "-" + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "d" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlists" + R_PAREN ")" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "e" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "e" + DOT "." + NAME_REF + NAME_KW "name" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "'Alice'" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "order_id" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "d" + DOT "." + NAME_REF + IDENT "wishlist_name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "e" + DOT "." + NAME_REF + NAME_KW "name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "e1" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_orders" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + PAREN_GRAPH_PATTERN + L_PAREN "(" + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "e2" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + R_PAREN ")" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + IDENT "price" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "50" + WHITESPACE "\n " + R_PAREN ")" + WHITESPACE "\n " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "b" + DOT "." + NAME_REF + IDENT "ordered_when" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + CAST_EXPR + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "date" + WHITESPACE " " + LITERAL + STRING "'2023-01-01'" + WHITESPACE "\n " + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "c" + DOT "." + NAME_REF + NAME_KW "name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + GRAPH_TABLE_FN + GRAPH_TABLE_KW "graph_table" + L_PAREN "(" + WHITESPACE "\n " + PATH + PATH_SEGMENT + NAME_REF + IDENT "myshop" + WHITESPACE " " + MATCH_KW "match" + WHITESPACE "\n " + PATH_PATTERN_LIST + PATH_PATTERN + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "a" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "b" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "c" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "wishlists" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_ANY + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "d" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "products" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_RIGHT + MINUS "-" + L_BRACK "[" + NAME + IDENT "e" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "order_items" + R_BRACK "]" + MINUS "-" + R_ANGLE ">" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "f" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "orders" + R_PAREN ")" + WHITESPACE "\n " + PATH_FACTOR + EDGE_LEFT + L_ANGLE "<" + MINUS "-" + L_BRACK "[" + NAME + IDENT "g" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customer_orders" + R_BRACK "]" + MINUS "-" + WHITESPACE "\n " + PATH_FACTOR + VERTEX_PATTERN + L_PAREN "(" + NAME + IDENT "h" + WHITESPACE " " + IS_LABEL_EXPRESSION + IS_KW "is" + WHITESPACE " " + NAME_REF + IDENT "customers" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "h" + DOT "." + NAME_REF + IDENT "id" + WHITESPACE " " + NEQB "<>" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + IDENT "id" + R_PAREN ")" + WHITESPACE "\n " + COLUMNS_KW "columns" + WHITESPACE " " + L_PAREN "(" + EXPR_AS_NAME_LIST + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "a" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "d" + DOT "." + NAME_REF + NAME_KW "name" + COMMA "," + WHITESPACE " " + EXPR_AS_NAME + FIELD_EXPR + NAME_REF + IDENT "h" + DOT "." + NAME_REF + NAME_KW "name" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/tests.rs b/crates/squawk_parser/tests/tests.rs index 71f6f951..72694dbc 100644 --- a/crates/squawk_parser/tests/tests.rs +++ b/crates/squawk_parser/tests/tests.rs @@ -34,17 +34,17 @@ fn parser_ok(fixture: Fixture<&str>) { errors.is_none(), "tests defined in the `ok` can't have parser errors." ); - // skipping pg17/pg18/pg19 specific stuff since our parser isn't using the latest parser - if !test_name.ends_with("pg17") && !test_name.ends_with("pg18") && !test_name.ends_with("pg19") - { - let pg_result = pg_query::parse(content); - if let Err(e) = &pg_result { - assert!( - &pg_result.is_ok(), - "tests defined in the `ok` can't have Postgres parser errors. Found {e}", - ); - } - } + // // skipping pg17/pg18/pg19 specific stuff since our parser isn't using the latest parser + // if !test_name.ends_with("pg17") && !test_name.ends_with("pg18") && !test_name.ends_with("pg19") + // { + // let pg_result = pg_query::parse(content); + // if let Err(e) = &pg_result { + // assert!( + // &pg_result.is_ok(), + // "tests defined in the `ok` can't have Postgres parser errors. Found {e}", + // ); + // } + // } } #[dir_test( diff --git a/crates/squawk_server/src/handlers/code_action.rs b/crates/squawk_server/src/handlers/code_action.rs index 9c247bbc..358262a1 100644 --- a/crates/squawk_server/src/handlers/code_action.rs +++ b/crates/squawk_server/src/handlers/code_action.rs @@ -55,18 +55,12 @@ pub(crate) fn handle_code_action( title: format!("Disable {rule_name} for this line"), kind: Some(CodeActionKind::QUICKFIX), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit { - changes: Some({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), vec![ignore_line_edit]); - changes.into_iter().collect() - }), - ..Default::default() - }), - command: None, - is_preferred: Some(false), - disabled: None, - data: None, + edit: Some(WorkspaceEdit::new({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), vec![ignore_line_edit]); + changes.into_iter().collect() + })), + ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(disable_line_action)); } @@ -75,18 +69,12 @@ pub(crate) fn handle_code_action( title: format!("Disable {rule_name} for the entire file"), kind: Some(CodeActionKind::QUICKFIX), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit { - changes: Some({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), vec![ignore_file_edit]); - changes.into_iter().collect() - }), - ..Default::default() - }), - command: None, - is_preferred: Some(false), - disabled: None, - data: None, + edit: Some(WorkspaceEdit::new({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), vec![ignore_file_edit]); + changes.into_iter().collect() + })), + ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(disable_file_action)); } @@ -96,7 +84,6 @@ pub(crate) fn handle_code_action( title: title.clone(), kind: Some(CodeActionKind::QUICKFIX), diagnostics: Some(vec![diagnostic.clone()]), - edit: None, command: Some(Command { title, command: "vscode.open".to_string(), @@ -104,9 +91,7 @@ pub(crate) fn handle_code_action( "https://squawkhq.com/docs/{rule_name}" ))?]), }), - is_preferred: Some(false), - disabled: None, - data: None, + ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(documentation_action)); @@ -115,18 +100,13 @@ pub(crate) fn handle_code_action( title: associated_data.title, kind: Some(CodeActionKind::QUICKFIX), diagnostics: Some(vec![diagnostic.clone()]), - edit: Some(WorkspaceEdit { - changes: Some({ - let mut changes = FxHashMap::default(); - changes.insert(uri.clone(), associated_data.edits); - changes.into_iter().collect() - }), - ..Default::default() - }), - command: None, + edit: Some(WorkspaceEdit::new({ + let mut changes = FxHashMap::default(); + changes.insert(uri.clone(), associated_data.edits); + changes.into_iter().collect() + })), is_preferred: Some(true), - disabled: None, - data: None, + ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(fix_action)); } diff --git a/crates/squawk_server/src/lsp_utils.rs b/crates/squawk_server/src/lsp_utils.rs index a677ff91..acbf3fb7 100644 --- a/crates/squawk_server/src/lsp_utils.rs +++ b/crates/squawk_server/src/lsp_utils.rs @@ -61,27 +61,21 @@ pub(crate) fn code_action( CodeAction { title: action.title, kind: Some(kind), - diagnostics: None, - edit: Some(WorkspaceEdit { - changes: Some({ - let mut changes = FxHashMap::default(); - let edits = action - .edits - .into_iter() - .map(|edit| lsp_types::TextEdit { - range: range(line_index, edit.text_range), - new_text: edit.text.unwrap_or_default(), - }) - .collect(); - changes.insert(uri, edits); - changes.into_iter().collect() - }), - ..Default::default() - }), - command: None, + edit: Some(WorkspaceEdit::new({ + let mut changes = FxHashMap::default(); + let edits = action + .edits + .into_iter() + .map(|edit| lsp_types::TextEdit { + range: range(line_index, edit.text_range), + new_text: edit.text.unwrap_or_default(), + }) + .collect(); + changes.insert(uri, edits); + changes.into_iter().collect() + })), is_preferred: Some(true), - disabled: None, - data: None, + ..Default::default() } } diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index 5314d316..604561b8 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -139,6 +139,29 @@ impl AddGenerated { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddLabel { + pub(crate) syntax: SyntaxNode, +} +impl AddLabel { + #[inline] + pub fn element_table_properties(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn add_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ADD_KW) + } + #[inline] + pub fn label_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LABEL_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AddOpClassOptions { pub(crate) syntax: SyntaxNode, @@ -181,6 +204,84 @@ impl AddValue { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddVertexEdgeLabelProperties { + pub(crate) syntax: SyntaxNode, +} +impl AddVertexEdgeLabelProperties { + #[inline] + pub fn expr_as_name_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn add_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ADD_KW) + } + #[inline] + pub fn alter_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALTER_KW) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn label_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LABEL_KW) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn properties_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTIES_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLE_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddVertexEdgeTables { + pub(crate) syntax: SyntaxNode, +} +impl AddVertexEdgeTables { + #[inline] + pub fn edge_tables(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn vertex_tables(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn add_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ADD_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AfterValue { pub(crate) syntax: SyntaxNode, @@ -257,6 +358,25 @@ impl AllFn { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AllProperties { + pub(crate) syntax: SyntaxNode, +} +impl AllProperties { + #[inline] + pub fn all_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALL_KW) + } + #[inline] + pub fn columns_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::COLUMNS_KW) + } + #[inline] + pub fn properties_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTIES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AlterAggregate { pub(crate) syntax: SyntaxNode, @@ -1183,6 +1303,37 @@ impl AlterProcedure { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterPropertyGraph { + pub(crate) syntax: SyntaxNode, +} +impl AlterPropertyGraph { + #[inline] + pub fn alter_property_graph_action(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn if_exists(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn alter_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALTER_KW) + } + #[inline] + pub fn graph_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GRAPH_KW) + } + #[inline] + pub fn property_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTY_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AlterPublication { pub(crate) syntax: SyntaxNode, @@ -1806,6 +1957,49 @@ impl AlterUserMapping { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterVertexEdgeLabels { + pub(crate) syntax: SyntaxNode, +} +impl AlterVertexEdgeLabels { + #[inline] + pub fn add_label(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn add_labels(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn alter_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALTER_KW) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLE_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AlterView { pub(crate) syntax: SyntaxNode, @@ -2360,6 +2554,10 @@ impl CallExpr { support::child(&self.syntax) } #[inline] + pub fn graph_table_fn(&self) -> Option { + support::child(&self.syntax) + } + #[inline] pub fn json_array_agg_fn(&self) -> Option { support::child(&self.syntax) } @@ -2979,6 +3177,10 @@ impl CommentOn { support::token(&self.syntax, SyntaxKind::FUNCTION_KW) } #[inline] + pub fn graph_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GRAPH_KW) + } + #[inline] pub fn index_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::INDEX_KW) } @@ -3035,6 +3237,10 @@ impl CommentOn { support::token(&self.syntax, SyntaxKind::PROCEDURE_KW) } #[inline] + pub fn property_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTY_KW) + } + #[inline] pub fn publication_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::PUBLICATION_KW) } @@ -4437,6 +4643,41 @@ impl CreateProcedure { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreatePropertyGraph { + pub(crate) syntax: SyntaxNode, +} +impl CreatePropertyGraph { + #[inline] + pub fn edge_tables(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn temp(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn vertex_tables(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn create_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CREATE_KW) + } + #[inline] + pub fn graph_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GRAPH_KW) + } + #[inline] + pub fn property_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTY_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CreatePublication { pub(crate) syntax: SyntaxNode, @@ -5751,6 +5992,45 @@ impl DependsOnExtension { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DestVertexTable { + pub(crate) syntax: SyntaxNode, +} +impl DestVertexTable { + #[inline] + pub fn column_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn destination_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DESTINATION_KW) + } + #[inline] + pub fn key_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::KEY_KW) + } + #[inline] + pub fn references_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::REFERENCES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DetachPartition { pub(crate) syntax: SyntaxNode, @@ -6243,6 +6523,49 @@ impl DropDomain { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropEdgeTables { + pub(crate) syntax: SyntaxNode, +} +impl DropEdgeTables { + #[inline] + pub fn names(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn cascade_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CASCADE_KW) + } + #[inline] + pub fn drop_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DROP_KW) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn restrict_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RESTRICT_KW) + } + #[inline] + pub fn tables_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DropEventTrigger { pub(crate) syntax: SyntaxNode, @@ -6862,6 +7185,41 @@ impl DropProcedure { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropPropertyGraph { + pub(crate) syntax: SyntaxNode, +} +impl DropPropertyGraph { + #[inline] + pub fn if_exists(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn cascade_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CASCADE_KW) + } + #[inline] + pub fn drop_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DROP_KW) + } + #[inline] + pub fn graph_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GRAPH_KW) + } + #[inline] + pub fn property_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTY_KW) + } + #[inline] + pub fn restrict_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RESTRICT_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DropPublication { pub(crate) syntax: SyntaxNode, @@ -7518,6 +7876,167 @@ impl DropUserMapping { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropVertexEdgeLabel { + pub(crate) syntax: SyntaxNode, +} +impl DropVertexEdgeLabel { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn alter_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALTER_KW) + } + #[inline] + pub fn cascade_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CASCADE_KW) + } + #[inline] + pub fn drop_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DROP_KW) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn label_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LABEL_KW) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn restrict_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RESTRICT_KW) + } + #[inline] + pub fn table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLE_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropVertexEdgeLabelProperties { + pub(crate) syntax: SyntaxNode, +} +impl DropVertexEdgeLabelProperties { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn names(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn alter_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ALTER_KW) + } + #[inline] + pub fn cascade_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CASCADE_KW) + } + #[inline] + pub fn drop_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DROP_KW) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn label_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LABEL_KW) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn properties_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTIES_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn restrict_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RESTRICT_KW) + } + #[inline] + pub fn table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLE_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropVertexTables { + pub(crate) syntax: SyntaxNode, +} +impl DropVertexTables { + #[inline] + pub fn names(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn cascade_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CASCADE_KW) + } + #[inline] + pub fn drop_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DROP_KW) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn restrict_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RESTRICT_KW) + } + #[inline] + pub fn tables_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLES_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DropView { pub(crate) syntax: SyntaxNode, @@ -7549,6 +8068,185 @@ impl DropView { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EdgeAny { + pub(crate) syntax: SyntaxNode, +} +impl EdgeAny { + #[inline] + pub fn is_label_expression(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_BRACK) + } + #[inline] + pub fn r_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_BRACK) + } + #[inline] + pub fn minus_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::MINUS) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EdgeLeft { + pub(crate) syntax: SyntaxNode, +} +impl EdgeLeft { + #[inline] + pub fn is_label_expression(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_BRACK) + } + #[inline] + pub fn r_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_BRACK) + } + #[inline] + pub fn minus_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::MINUS) + } + #[inline] + pub fn l_angle_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_ANGLE) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EdgeRight { + pub(crate) syntax: SyntaxNode, +} +impl EdgeRight { + #[inline] + pub fn is_label_expression(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_BRACK) + } + #[inline] + pub fn r_brack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_BRACK) + } + #[inline] + pub fn minus_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::MINUS) + } + #[inline] + pub fn r_angle_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_ANGLE) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EdgeTableDef { + pub(crate) syntax: SyntaxNode, +} +impl EdgeTableDef { + #[inline] + pub fn column_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn dest_vertex_table(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn element_table_label_and_properties(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn source_vertex_table(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn as_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::AS_KW) + } + #[inline] + pub fn key_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::KEY_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EdgeTables { + pub(crate) syntax: SyntaxNode, +} +impl EdgeTables { + #[inline] + pub fn edge_table_defs(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn edge_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EDGE_KW) + } + #[inline] + pub fn relationship_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RELATIONSHIP_KW) + } + #[inline] + pub fn tables_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ElseClause { pub(crate) syntax: SyntaxNode, @@ -7899,6 +8597,17 @@ impl ExprAsName { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExprAsNameList { + pub(crate) syntax: SyntaxNode, +} +impl ExprAsNameList { + #[inline] + pub fn expr_as_names(&self) -> AstChildren { + support::children(&self.syntax) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ExprType { pub(crate) syntax: SyntaxNode, @@ -8569,6 +9278,72 @@ impl GrantDefaultPrivileges { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GraphPatternQualifier { + pub(crate) syntax: SyntaxNode, +} +impl GraphPatternQualifier { + #[inline] + pub fn literal(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_curly_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_CURLY) + } + #[inline] + pub fn r_curly_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_CURLY) + } + #[inline] + pub fn comma_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::COMMA) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GraphTableFn { + pub(crate) syntax: SyntaxNode, +} +impl GraphTableFn { + #[inline] + pub fn expr_as_name_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path_pattern_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn columns_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::COLUMNS_KW) + } + #[inline] + pub fn graph_table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GRAPH_TABLE_KW) + } + #[inline] + pub fn match_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::MATCH_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GroupByClause { pub(crate) syntax: SyntaxNode, @@ -9181,6 +9956,21 @@ impl IsJsonValue { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IsLabelExpression { + pub(crate) syntax: SyntaxNode, +} +impl IsLabelExpression { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn is_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::IS_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IsNormalized { pub(crate) syntax: SyntaxNode, @@ -10462,6 +11252,48 @@ impl JsonWrapperBehaviorClause { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LabelAndProperties { + pub(crate) syntax: SyntaxNode, +} +impl LabelAndProperties { + #[inline] + pub fn element_table_properties(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn default_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DEFAULT_KW) + } + #[inline] + pub fn label_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LABEL_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LabelAndPropertiesList { + pub(crate) syntax: SyntaxNode, +} +impl LabelAndPropertiesList { + #[inline] + pub fn label_and_propertiess(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct LanguageFuncOption { pub(crate) syntax: SyntaxNode, @@ -11213,6 +12045,21 @@ impl NoInheritTable { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NoProperties { + pub(crate) syntax: SyntaxNode, +} +impl NoProperties { + #[inline] + pub fn no_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NO_KW) + } + #[inline] + pub fn properties_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTIES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct NonStandardParam { pub(crate) syntax: SyntaxNode, @@ -12316,6 +13163,29 @@ impl ParenExpr { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParenGraphPattern { + pub(crate) syntax: SyntaxNode, +} +impl ParenGraphPattern { + #[inline] + pub fn path_pattern(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParenSelect { pub(crate) syntax: SyntaxNode, @@ -12592,6 +13462,47 @@ impl Path { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathFactor { + pub(crate) syntax: SyntaxNode, +} +impl PathFactor { + #[inline] + pub fn graph_pattern_qualifier(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path_primary(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathPattern { + pub(crate) syntax: SyntaxNode, +} +impl PathPattern { + #[inline] + pub fn path_factor(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path_factors(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathPatternList { + pub(crate) syntax: SyntaxNode, +} +impl PathPatternList { + #[inline] + pub fn path_patterns(&self) -> AstChildren { + support::children(&self.syntax) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PathSegment { pub(crate) syntax: SyntaxNode, @@ -12851,6 +13762,29 @@ impl Privileges { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PropertiesList { + pub(crate) syntax: SyntaxNode, +} +impl PropertiesList { + #[inline] + pub fn expr_as_name_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn properties_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::PROPERTIES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PublicationObject { pub(crate) syntax: SyntaxNode, @@ -13367,6 +14301,37 @@ impl RenameValue { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Repack { + pub(crate) syntax: SyntaxNode, +} +impl Repack { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn option_item_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn table_and_columns_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn index_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::INDEX_KW) + } + #[inline] + pub fn repack_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::REPACK_KW) + } + #[inline] + pub fn using_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::USING_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RepeatableClause { pub(crate) syntax: SyntaxNode, @@ -15393,6 +16358,45 @@ impl SourceFile { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SourceVertexTable { + pub(crate) syntax: SyntaxNode, +} +impl SourceVertexTable { + #[inline] + pub fn column_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn key_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::KEY_KW) + } + #[inline] + pub fn references_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::REFERENCES_KW) + } + #[inline] + pub fn source_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::SOURCE_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct SplitPartition { pub(crate) syntax: SyntaxNode, @@ -15663,6 +16667,33 @@ impl TargetList { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Temp { + pub(crate) syntax: SyntaxNode, +} +impl Temp { + #[inline] + pub fn global_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GLOBAL_KW) + } + #[inline] + pub fn local_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::LOCAL_KW) + } + #[inline] + pub fn temp_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TEMP_KW) + } + #[inline] + pub fn temporary_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TEMPORARY_KW) + } + #[inline] + pub fn unlogged_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::UNLOGGED_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TimeType { pub(crate) syntax: SyntaxNode, @@ -16287,6 +17318,103 @@ impl VariantList { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VertexPattern { + pub(crate) syntax: SyntaxNode, +} +impl VertexPattern { + #[inline] + pub fn is_label_expression(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn where_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VertexTableDef { + pub(crate) syntax: SyntaxNode, +} +impl VertexTableDef { + #[inline] + pub fn column_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn element_table_label_and_properties(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn as_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::AS_KW) + } + #[inline] + pub fn key_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::KEY_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VertexTables { + pub(crate) syntax: SyntaxNode, +} +impl VertexTables { + #[inline] + pub fn vertex_table_defs(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] + pub fn node_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NODE_KW) + } + #[inline] + pub fn tables_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLES_KW) + } + #[inline] + pub fn vertex_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::VERTEX_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VolatilityFuncOption { pub(crate) syntax: SyntaxNode, @@ -16733,17 +17861,6 @@ impl WithoutTimezone { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct XmlAttributeList { - pub(crate) syntax: SyntaxNode, -} -impl XmlAttributeList { - #[inline] - pub fn expr_as_names(&self) -> AstChildren { - support::children(&self.syntax) - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct XmlColumnOption { pub(crate) syntax: SyntaxNode, @@ -16795,6 +17912,10 @@ pub struct XmlElementFn { pub(crate) syntax: SyntaxNode, } impl XmlElementFn { + #[inline] + pub fn expr_as_name_list(&self) -> Option { + support::child(&self.syntax) + } #[inline] pub fn exprs(&self) -> AstChildren { support::children(&self.syntax) @@ -16804,10 +17925,6 @@ impl XmlElementFn { support::child(&self.syntax) } #[inline] - pub fn xml_attribute_list(&self) -> Option { - support::child(&self.syntax) - } - #[inline] pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::L_PAREN) } @@ -16870,7 +17987,7 @@ pub struct XmlForestFn { } impl XmlForestFn { #[inline] - pub fn xml_attribute_list(&self) -> Option { + pub fn expr_as_name_list(&self) -> Option { support::child(&self.syntax) } #[inline] @@ -17282,6 +18399,20 @@ pub enum AlterMaterializedViewAction { AlterTableAction(AlterTableAction), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AlterPropertyGraphAction { + AddVertexEdgeLabelProperties(AddVertexEdgeLabelProperties), + AddVertexEdgeTables(AddVertexEdgeTables), + AlterVertexEdgeLabels(AlterVertexEdgeLabels), + DropEdgeTables(DropEdgeTables), + DropVertexEdgeLabel(DropVertexEdgeLabel), + DropVertexEdgeLabelProperties(DropVertexEdgeLabelProperties), + DropVertexTables(DropVertexTables), + OwnerTo(OwnerTo), + RenameTo(RenameTo), + SetSchema(SetSchema), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AlterTableAction { AddColumn(AddColumn), @@ -17379,6 +18510,19 @@ pub enum Constraint { UniqueConstraint(UniqueConstraint), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ElementTableLabelAndProperties { + LabelAndPropertiesList(LabelAndPropertiesList), + ElementTableProperties(ElementTableProperties), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ElementTableProperties { + AllProperties(AllProperties), + NoProperties(NoProperties), + PropertiesList(PropertiesList), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ExplainStmt { CompoundSelect(CompoundSelect), @@ -17510,6 +18654,15 @@ pub enum PartitionType { PartitionForValuesWith(PartitionForValuesWith), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum PathPrimary { + EdgeAny(EdgeAny), + EdgeLeft(EdgeLeft), + EdgeRight(EdgeRight), + ParenGraphPattern(ParenGraphPattern), + VertexPattern(VertexPattern), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PreparableStmt { CompoundSelect(CompoundSelect), @@ -17581,6 +18734,7 @@ pub enum Stmt { AlterOperatorFamily(AlterOperatorFamily), AlterPolicy(AlterPolicy), AlterProcedure(AlterProcedure), + AlterPropertyGraph(AlterPropertyGraph), AlterPublication(AlterPublication), AlterRole(AlterRole), AlterRoutine(AlterRoutine), @@ -17632,6 +18786,7 @@ pub enum Stmt { CreateOperatorFamily(CreateOperatorFamily), CreatePolicy(CreatePolicy), CreateProcedure(CreateProcedure), + CreatePropertyGraph(CreatePropertyGraph), CreatePublication(CreatePublication), CreateRole(CreateRole), CreateRule(CreateRule), @@ -17680,6 +18835,7 @@ pub enum Stmt { DropOwned(DropOwned), DropPolicy(DropPolicy), DropProcedure(DropProcedure), + DropPropertyGraph(DropPropertyGraph), DropPublication(DropPublication), DropRole(DropRole), DropRoutine(DropRoutine), @@ -17720,6 +18876,7 @@ pub enum Stmt { Refresh(Refresh), Reindex(Reindex), ReleaseSavepoint(ReleaseSavepoint), + Repack(Repack), Reset(Reset), ResetSessionAuth(ResetSessionAuth), Revoke(Revoke), @@ -17879,6 +19036,24 @@ impl AstNode for AddGenerated { &self.syntax } } +impl AstNode for AddLabel { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_LABEL + } + #[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 AddOpClassOptions { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -17915,6 +19090,42 @@ impl AstNode for AddValue { &self.syntax } } +impl AstNode for AddVertexEdgeLabelProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_VERTEX_EDGE_LABEL_PROPERTIES + } + #[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 AddVertexEdgeTables { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_VERTEX_EDGE_TABLES + } + #[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 AfterValue { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -17987,6 +19198,24 @@ impl AstNode for AllFn { &self.syntax } } +impl AstNode for AllProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALL_PROPERTIES + } + #[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 AlterAggregate { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -18455,6 +19684,24 @@ impl AstNode for AlterProcedure { &self.syntax } } +impl AstNode for AlterPropertyGraph { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_PROPERTY_GRAPH + } + #[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 AlterPublication { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -18833,6 +20080,24 @@ impl AstNode for AlterUserMapping { &self.syntax } } +impl AstNode for AlterVertexEdgeLabels { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_VERTEX_EDGE_LABELS + } + #[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 AlterView { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -20345,6 +21610,24 @@ impl AstNode for CreateProcedure { &self.syntax } } +impl AstNode for CreatePropertyGraph { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_PROPERTY_GRAPH + } + #[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 CreatePublication { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -20885,6 +22168,24 @@ impl AstNode for DependsOnExtension { &self.syntax } } +impl AstNode for DestVertexTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DEST_VERTEX_TABLE + } + #[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 DetachPartition { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -21245,6 +22546,24 @@ impl AstNode for DropDomain { &self.syntax } } +impl AstNode for DropEdgeTables { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_EDGE_TABLES + } + #[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 DropEventTrigger { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -21623,6 +22942,24 @@ impl AstNode for DropProcedure { &self.syntax } } +impl AstNode for DropPropertyGraph { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_PROPERTY_GRAPH + } + #[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 DropPublication { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -21983,6 +23320,60 @@ impl AstNode for DropUserMapping { &self.syntax } } +impl AstNode for DropVertexEdgeLabel { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_VERTEX_EDGE_LABEL + } + #[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 DropVertexEdgeLabelProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_VERTEX_EDGE_LABEL_PROPERTIES + } + #[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 DropVertexTables { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_VERTEX_TABLES + } + #[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 DropView { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -22001,6 +23392,96 @@ impl AstNode for DropView { &self.syntax } } +impl AstNode for EdgeAny { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EDGE_ANY + } + #[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 EdgeLeft { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EDGE_LEFT + } + #[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 EdgeRight { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EDGE_RIGHT + } + #[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 EdgeTableDef { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EDGE_TABLE_DEF + } + #[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 EdgeTables { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EDGE_TABLES + } + #[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 ElseClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -22325,6 +23806,24 @@ impl AstNode for ExprAsName { &self.syntax } } +impl AstNode for ExprAsNameList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EXPR_AS_NAME_LIST + } + #[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 ExprType { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -22721,6 +24220,42 @@ impl AstNode for GrantDefaultPrivileges { &self.syntax } } +impl AstNode for GraphPatternQualifier { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::GRAPH_PATTERN_QUALIFIER + } + #[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 GraphTableFn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::GRAPH_TABLE_FN + } + #[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 GroupByClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -23225,6 +24760,24 @@ impl AstNode for IsJsonValue { &self.syntax } } +impl AstNode for IsLabelExpression { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::IS_LABEL_EXPRESSION + } + #[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 IsNormalized { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -24215,6 +25768,42 @@ impl AstNode for JsonWrapperBehaviorClause { &self.syntax } } +impl AstNode for LabelAndProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LABEL_AND_PROPERTIES + } + #[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 LabelAndPropertiesList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LABEL_AND_PROPERTIES_LIST + } + #[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 LanguageFuncOption { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -24863,6 +26452,24 @@ impl AstNode for NoInheritTable { &self.syntax } } +impl AstNode for NoProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NO_PROPERTIES + } + #[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 NonStandardParam { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -25745,6 +27352,24 @@ impl AstNode for ParenExpr { &self.syntax } } +impl AstNode for ParenGraphPattern { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PAREN_GRAPH_PATTERN + } + #[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 ParenSelect { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -25961,6 +27586,60 @@ impl AstNode for Path { &self.syntax } } +impl AstNode for PathFactor { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH_FACTOR + } + #[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 PathPattern { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH_PATTERN + } + #[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 PathPatternList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH_PATTERN_LIST + } + #[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 PathSegment { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -26195,6 +27874,24 @@ impl AstNode for Privileges { &self.syntax } } +impl AstNode for PropertiesList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PROPERTIES_LIST + } + #[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 PublicationObject { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -26555,6 +28252,24 @@ impl AstNode for RenameValue { &self.syntax } } +impl AstNode for Repack { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::REPACK + } + #[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 RepeatableClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -28031,6 +29746,24 @@ impl AstNode for SourceFile { &self.syntax } } +impl AstNode for SourceVertexTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SOURCE_VERTEX_TABLE + } + #[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 SplitPartition { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -28283,6 +30016,24 @@ impl AstNode for TargetList { &self.syntax } } +impl AstNode for Temp { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::TEMP + } + #[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 TimeType { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -28787,6 +30538,60 @@ impl AstNode for VariantList { &self.syntax } } +impl AstNode for VertexPattern { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::VERTEX_PATTERN + } + #[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 VertexTableDef { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::VERTEX_TABLE_DEF + } + #[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 VertexTables { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::VERTEX_TABLES + } + #[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 VolatilityFuncOption { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -29183,24 +30988,6 @@ impl AstNode for WithoutTimezone { &self.syntax } } -impl AstNode for XmlAttributeList { - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { - kind == SyntaxKind::XML_ATTRIBUTE_LIST - } - #[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 XmlColumnOption { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -30022,6 +31809,136 @@ impl From for AlterMaterializedViewAction { AlterMaterializedViewAction::SetSchema(node) } } +impl AstNode for AlterPropertyGraphAction { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::ADD_VERTEX_EDGE_LABEL_PROPERTIES + | SyntaxKind::ADD_VERTEX_EDGE_TABLES + | SyntaxKind::ALTER_VERTEX_EDGE_LABELS + | SyntaxKind::DROP_EDGE_TABLES + | SyntaxKind::DROP_VERTEX_EDGE_LABEL + | SyntaxKind::DROP_VERTEX_EDGE_LABEL_PROPERTIES + | SyntaxKind::DROP_VERTEX_TABLES + | SyntaxKind::OWNER_TO + | SyntaxKind::RENAME_TO + | SyntaxKind::SET_SCHEMA + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::ADD_VERTEX_EDGE_LABEL_PROPERTIES => { + AlterPropertyGraphAction::AddVertexEdgeLabelProperties( + AddVertexEdgeLabelProperties { syntax }, + ) + } + SyntaxKind::ADD_VERTEX_EDGE_TABLES => { + AlterPropertyGraphAction::AddVertexEdgeTables(AddVertexEdgeTables { syntax }) + } + SyntaxKind::ALTER_VERTEX_EDGE_LABELS => { + AlterPropertyGraphAction::AlterVertexEdgeLabels(AlterVertexEdgeLabels { syntax }) + } + SyntaxKind::DROP_EDGE_TABLES => { + AlterPropertyGraphAction::DropEdgeTables(DropEdgeTables { syntax }) + } + SyntaxKind::DROP_VERTEX_EDGE_LABEL => { + AlterPropertyGraphAction::DropVertexEdgeLabel(DropVertexEdgeLabel { syntax }) + } + SyntaxKind::DROP_VERTEX_EDGE_LABEL_PROPERTIES => { + AlterPropertyGraphAction::DropVertexEdgeLabelProperties( + DropVertexEdgeLabelProperties { syntax }, + ) + } + SyntaxKind::DROP_VERTEX_TABLES => { + AlterPropertyGraphAction::DropVertexTables(DropVertexTables { syntax }) + } + SyntaxKind::OWNER_TO => AlterPropertyGraphAction::OwnerTo(OwnerTo { syntax }), + SyntaxKind::RENAME_TO => AlterPropertyGraphAction::RenameTo(RenameTo { syntax }), + SyntaxKind::SET_SCHEMA => AlterPropertyGraphAction::SetSchema(SetSchema { syntax }), + _ => { + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AlterPropertyGraphAction::AddVertexEdgeLabelProperties(it) => &it.syntax, + AlterPropertyGraphAction::AddVertexEdgeTables(it) => &it.syntax, + AlterPropertyGraphAction::AlterVertexEdgeLabels(it) => &it.syntax, + AlterPropertyGraphAction::DropEdgeTables(it) => &it.syntax, + AlterPropertyGraphAction::DropVertexEdgeLabel(it) => &it.syntax, + AlterPropertyGraphAction::DropVertexEdgeLabelProperties(it) => &it.syntax, + AlterPropertyGraphAction::DropVertexTables(it) => &it.syntax, + AlterPropertyGraphAction::OwnerTo(it) => &it.syntax, + AlterPropertyGraphAction::RenameTo(it) => &it.syntax, + AlterPropertyGraphAction::SetSchema(it) => &it.syntax, + } + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: AddVertexEdgeLabelProperties) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::AddVertexEdgeLabelProperties(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: AddVertexEdgeTables) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::AddVertexEdgeTables(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: AlterVertexEdgeLabels) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::AlterVertexEdgeLabels(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: DropEdgeTables) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::DropEdgeTables(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: DropVertexEdgeLabel) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::DropVertexEdgeLabel(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: DropVertexEdgeLabelProperties) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::DropVertexEdgeLabelProperties(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: DropVertexTables) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::DropVertexTables(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: OwnerTo) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::OwnerTo(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: RenameTo) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::RenameTo(node) + } +} +impl From for AlterPropertyGraphAction { + #[inline] + fn from(node: SetSchema) -> AlterPropertyGraphAction { + AlterPropertyGraphAction::SetSchema(node) + } +} impl AstNode for AlterTableAction { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -30868,6 +32785,97 @@ impl From for Constraint { Constraint::UniqueConstraint(node) } } +impl AstNode for ElementTableLabelAndProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, SyntaxKind::LABEL_AND_PROPERTIES_LIST) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::LABEL_AND_PROPERTIES_LIST => { + ElementTableLabelAndProperties::LabelAndPropertiesList(LabelAndPropertiesList { + syntax, + }) + } + _ => { + if let Some(result) = ElementTableProperties::cast(syntax) { + return Some(ElementTableLabelAndProperties::ElementTableProperties( + result, + )); + } + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + ElementTableLabelAndProperties::LabelAndPropertiesList(it) => &it.syntax, + ElementTableLabelAndProperties::ElementTableProperties(it) => it.syntax(), + } + } +} +impl From for ElementTableLabelAndProperties { + #[inline] + fn from(node: LabelAndPropertiesList) -> ElementTableLabelAndProperties { + ElementTableLabelAndProperties::LabelAndPropertiesList(node) + } +} +impl AstNode for ElementTableProperties { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::ALL_PROPERTIES | SyntaxKind::NO_PROPERTIES | SyntaxKind::PROPERTIES_LIST + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::ALL_PROPERTIES => { + ElementTableProperties::AllProperties(AllProperties { syntax }) + } + SyntaxKind::NO_PROPERTIES => { + ElementTableProperties::NoProperties(NoProperties { syntax }) + } + SyntaxKind::PROPERTIES_LIST => { + ElementTableProperties::PropertiesList(PropertiesList { syntax }) + } + _ => { + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + ElementTableProperties::AllProperties(it) => &it.syntax, + ElementTableProperties::NoProperties(it) => &it.syntax, + ElementTableProperties::PropertiesList(it) => &it.syntax, + } + } +} +impl From for ElementTableProperties { + #[inline] + fn from(node: AllProperties) -> ElementTableProperties { + ElementTableProperties::AllProperties(node) + } +} +impl From for ElementTableProperties { + #[inline] + fn from(node: NoProperties) -> ElementTableProperties { + ElementTableProperties::NoProperties(node) + } +} +impl From for ElementTableProperties { + #[inline] + fn from(node: PropertiesList) -> ElementTableProperties { + ElementTableProperties::PropertiesList(node) + } +} impl AstNode for ExplainStmt { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -31931,6 +33939,75 @@ impl From for PartitionType { PartitionType::PartitionForValuesWith(node) } } +impl AstNode for PathPrimary { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::EDGE_ANY + | SyntaxKind::EDGE_LEFT + | SyntaxKind::EDGE_RIGHT + | SyntaxKind::PAREN_GRAPH_PATTERN + | SyntaxKind::VERTEX_PATTERN + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::EDGE_ANY => PathPrimary::EdgeAny(EdgeAny { syntax }), + SyntaxKind::EDGE_LEFT => PathPrimary::EdgeLeft(EdgeLeft { syntax }), + SyntaxKind::EDGE_RIGHT => PathPrimary::EdgeRight(EdgeRight { syntax }), + SyntaxKind::PAREN_GRAPH_PATTERN => { + PathPrimary::ParenGraphPattern(ParenGraphPattern { syntax }) + } + SyntaxKind::VERTEX_PATTERN => PathPrimary::VertexPattern(VertexPattern { syntax }), + _ => { + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + PathPrimary::EdgeAny(it) => &it.syntax, + PathPrimary::EdgeLeft(it) => &it.syntax, + PathPrimary::EdgeRight(it) => &it.syntax, + PathPrimary::ParenGraphPattern(it) => &it.syntax, + PathPrimary::VertexPattern(it) => &it.syntax, + } + } +} +impl From for PathPrimary { + #[inline] + fn from(node: EdgeAny) -> PathPrimary { + PathPrimary::EdgeAny(node) + } +} +impl From for PathPrimary { + #[inline] + fn from(node: EdgeLeft) -> PathPrimary { + PathPrimary::EdgeLeft(node) + } +} +impl From for PathPrimary { + #[inline] + fn from(node: EdgeRight) -> PathPrimary { + PathPrimary::EdgeRight(node) + } +} +impl From for PathPrimary { + #[inline] + fn from(node: ParenGraphPattern) -> PathPrimary { + PathPrimary::ParenGraphPattern(node) + } +} +impl From for PathPrimary { + #[inline] + fn from(node: VertexPattern) -> PathPrimary { + PathPrimary::VertexPattern(node) + } +} impl AstNode for PreparableStmt { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -32324,6 +34401,7 @@ impl AstNode for Stmt { | SyntaxKind::ALTER_OPERATOR_FAMILY | SyntaxKind::ALTER_POLICY | SyntaxKind::ALTER_PROCEDURE + | SyntaxKind::ALTER_PROPERTY_GRAPH | SyntaxKind::ALTER_PUBLICATION | SyntaxKind::ALTER_ROLE | SyntaxKind::ALTER_ROUTINE @@ -32375,6 +34453,7 @@ impl AstNode for Stmt { | SyntaxKind::CREATE_OPERATOR_FAMILY | SyntaxKind::CREATE_POLICY | SyntaxKind::CREATE_PROCEDURE + | SyntaxKind::CREATE_PROPERTY_GRAPH | SyntaxKind::CREATE_PUBLICATION | SyntaxKind::CREATE_ROLE | SyntaxKind::CREATE_RULE @@ -32423,6 +34502,7 @@ impl AstNode for Stmt { | SyntaxKind::DROP_OWNED | SyntaxKind::DROP_POLICY | SyntaxKind::DROP_PROCEDURE + | SyntaxKind::DROP_PROPERTY_GRAPH | SyntaxKind::DROP_PUBLICATION | SyntaxKind::DROP_ROLE | SyntaxKind::DROP_ROUTINE @@ -32463,6 +34543,7 @@ impl AstNode for Stmt { | SyntaxKind::REFRESH | SyntaxKind::REINDEX | SyntaxKind::RELEASE_SAVEPOINT + | SyntaxKind::REPACK | SyntaxKind::RESET | SyntaxKind::RESET_SESSION_AUTH | SyntaxKind::REVOKE @@ -32523,6 +34604,9 @@ impl AstNode for Stmt { } SyntaxKind::ALTER_POLICY => Stmt::AlterPolicy(AlterPolicy { syntax }), SyntaxKind::ALTER_PROCEDURE => Stmt::AlterProcedure(AlterProcedure { syntax }), + SyntaxKind::ALTER_PROPERTY_GRAPH => { + Stmt::AlterPropertyGraph(AlterPropertyGraph { syntax }) + } SyntaxKind::ALTER_PUBLICATION => Stmt::AlterPublication(AlterPublication { syntax }), SyntaxKind::ALTER_ROLE => Stmt::AlterRole(AlterRole { syntax }), SyntaxKind::ALTER_ROUTINE => Stmt::AlterRoutine(AlterRoutine { syntax }), @@ -32596,6 +34680,9 @@ impl AstNode for Stmt { } SyntaxKind::CREATE_POLICY => Stmt::CreatePolicy(CreatePolicy { syntax }), SyntaxKind::CREATE_PROCEDURE => Stmt::CreateProcedure(CreateProcedure { syntax }), + SyntaxKind::CREATE_PROPERTY_GRAPH => { + Stmt::CreatePropertyGraph(CreatePropertyGraph { syntax }) + } SyntaxKind::CREATE_PUBLICATION => Stmt::CreatePublication(CreatePublication { syntax }), SyntaxKind::CREATE_ROLE => Stmt::CreateRole(CreateRole { syntax }), SyntaxKind::CREATE_RULE => Stmt::CreateRule(CreateRule { syntax }), @@ -32664,6 +34751,9 @@ impl AstNode for Stmt { SyntaxKind::DROP_OWNED => Stmt::DropOwned(DropOwned { syntax }), SyntaxKind::DROP_POLICY => Stmt::DropPolicy(DropPolicy { syntax }), SyntaxKind::DROP_PROCEDURE => Stmt::DropProcedure(DropProcedure { syntax }), + SyntaxKind::DROP_PROPERTY_GRAPH => { + Stmt::DropPropertyGraph(DropPropertyGraph { syntax }) + } SyntaxKind::DROP_PUBLICATION => Stmt::DropPublication(DropPublication { syntax }), SyntaxKind::DROP_ROLE => Stmt::DropRole(DropRole { syntax }), SyntaxKind::DROP_ROUTINE => Stmt::DropRoutine(DropRoutine { syntax }), @@ -32716,6 +34806,7 @@ impl AstNode for Stmt { SyntaxKind::REFRESH => Stmt::Refresh(Refresh { syntax }), SyntaxKind::REINDEX => Stmt::Reindex(Reindex { syntax }), SyntaxKind::RELEASE_SAVEPOINT => Stmt::ReleaseSavepoint(ReleaseSavepoint { syntax }), + SyntaxKind::REPACK => Stmt::Repack(Repack { syntax }), SyntaxKind::RESET => Stmt::Reset(Reset { syntax }), SyntaxKind::RESET_SESSION_AUTH => Stmt::ResetSessionAuth(ResetSessionAuth { syntax }), SyntaxKind::REVOKE => Stmt::Revoke(Revoke { syntax }), @@ -32766,6 +34857,7 @@ impl AstNode for Stmt { Stmt::AlterOperatorFamily(it) => &it.syntax, Stmt::AlterPolicy(it) => &it.syntax, Stmt::AlterProcedure(it) => &it.syntax, + Stmt::AlterPropertyGraph(it) => &it.syntax, Stmt::AlterPublication(it) => &it.syntax, Stmt::AlterRole(it) => &it.syntax, Stmt::AlterRoutine(it) => &it.syntax, @@ -32817,6 +34909,7 @@ impl AstNode for Stmt { Stmt::CreateOperatorFamily(it) => &it.syntax, Stmt::CreatePolicy(it) => &it.syntax, Stmt::CreateProcedure(it) => &it.syntax, + Stmt::CreatePropertyGraph(it) => &it.syntax, Stmt::CreatePublication(it) => &it.syntax, Stmt::CreateRole(it) => &it.syntax, Stmt::CreateRule(it) => &it.syntax, @@ -32865,6 +34958,7 @@ impl AstNode for Stmt { Stmt::DropOwned(it) => &it.syntax, Stmt::DropPolicy(it) => &it.syntax, Stmt::DropProcedure(it) => &it.syntax, + Stmt::DropPropertyGraph(it) => &it.syntax, Stmt::DropPublication(it) => &it.syntax, Stmt::DropRole(it) => &it.syntax, Stmt::DropRoutine(it) => &it.syntax, @@ -32905,6 +34999,7 @@ impl AstNode for Stmt { Stmt::Refresh(it) => &it.syntax, Stmt::Reindex(it) => &it.syntax, Stmt::ReleaseSavepoint(it) => &it.syntax, + Stmt::Repack(it) => &it.syntax, Stmt::Reset(it) => &it.syntax, Stmt::ResetSessionAuth(it) => &it.syntax, Stmt::Revoke(it) => &it.syntax, @@ -33054,6 +35149,12 @@ impl From for Stmt { Stmt::AlterProcedure(node) } } +impl From for Stmt { + #[inline] + fn from(node: AlterPropertyGraph) -> Stmt { + Stmt::AlterPropertyGraph(node) + } +} impl From for Stmt { #[inline] fn from(node: AlterPublication) -> Stmt { @@ -33360,6 +35461,12 @@ impl From for Stmt { Stmt::CreateProcedure(node) } } +impl From for Stmt { + #[inline] + fn from(node: CreatePropertyGraph) -> Stmt { + Stmt::CreatePropertyGraph(node) + } +} impl From for Stmt { #[inline] fn from(node: CreatePublication) -> Stmt { @@ -33648,6 +35755,12 @@ impl From for Stmt { Stmt::DropProcedure(node) } } +impl From for Stmt { + #[inline] + fn from(node: DropPropertyGraph) -> Stmt { + Stmt::DropPropertyGraph(node) + } +} impl From for Stmt { #[inline] fn from(node: DropPublication) -> Stmt { @@ -33888,6 +36001,12 @@ impl From for Stmt { Stmt::ReleaseSavepoint(node) } } +impl From for Stmt { + #[inline] + fn from(node: Repack) -> Stmt { + Stmt::Repack(node) + } +} impl From for Stmt { #[inline] fn from(node: Reset) -> Stmt { diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index fae16493..71301ab9 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -65,6 +65,7 @@ ArgList = CallExpr = Expr ArgList WithinClause? FilterClause? OverClause? | ExtractFn +| GraphTableFn | JsonExistsFn | JsonArrayFn | JsonObjectFn @@ -289,7 +290,7 @@ XmlElementFn = Name ( ',' 'xmlattributes' '(' - XmlAttributeList + ExprAsNameList ')' (',' (Expr (',' Expr)*))? | ',' (Expr (',' Expr)*) @@ -299,11 +300,11 @@ XmlElementFn = ExprAsName = Expr AsName? -XmlAttributeList = +ExprAsNameList = (ExprAsName (',' ExprAsName)*) XmlForestFn = - 'xmlforest' '(' XmlAttributeList ')' + 'xmlforest' '(' ExprAsNameList ')' XmlExistsFn = 'xmlexists' '(' @@ -2199,6 +2200,7 @@ CommentOn = | 'operator' 'class' Path UsingMethod | 'operator' 'family' Path UsingMethod | 'policy' NameRef 'on' Path + | 'property' 'graph' Path | 'procedural'? 'language' Path | 'procedure' FunctionSig | 'publication' Path @@ -2230,6 +2232,185 @@ Cluster = Path? UsingMethod? +Repack = + 'repack' + OptionItemList? + TableAndColumnsList? + ( + 'using' 'index' NameRef? + )? + +// TODO: use this everywhere +Temp = + 'temporary' +| 'temp' +| 'local' 'temporary' +| 'local' 'temp' +| 'global' 'temporary' +| 'global' 'temp' +| 'unlogged' + +CreatePropertyGraph = + 'create' Temp? 'property' 'graph' Path + VertexTables? + EdgeTables? + +VertexTables = + ('vertex' | 'node') 'tables' '(' (VertexTableDef (',' VertexTableDef)*) ')' + +VertexTableDef = + Path + ('as' Name)? + ('key' '(' ColumnList ')')? + ElementTableLabelAndProperties? + +ElementTableLabelAndProperties = + ElementTableProperties +| LabelAndPropertiesList + +ElementTableProperties = + NoProperties +| AllProperties +| PropertiesList + +NoProperties = + 'no' 'properties' + +AllProperties = + 'properties' 'all' 'columns' + +PropertiesList = + 'properties' '(' ExprAsNameList ')' + +LabelAndPropertiesList = + (LabelAndProperties (',' LabelAndProperties)*) + +LabelAndProperties = + ( + 'label' Name + | 'default' 'label' + ) + ElementTableProperties? + +EdgeTables = + ('edge' | 'relationship') 'tables' '(' (EdgeTableDef (',' EdgeTableDef)*) ')' + +EdgeTableDef = + Path + ('as' Name)? + ('key' '(' ColumnList ')')? + SourceVertexTable + DestVertexTable + ElementTableLabelAndProperties? + +SourceVertexTable = + 'source' NameRef +| 'source' 'key' '(' ColumnList ')' 'references' NameRef '(' ColumnList ')' + +DestVertexTable = + 'destination' NameRef +| 'destination' 'key' '(' ColumnList ')' 'references' NameRef '(' ColumnList ')' + +AlterPropertyGraph = + 'alter' 'property' 'graph' IfExists? Path + AlterPropertyGraphAction + +AlterPropertyGraphAction = + RenameTo +| SetSchema +| OwnerTo +| AddVertexEdgeTables +| DropVertexTables +| DropEdgeTables +| AlterVertexEdgeLabels +| DropVertexEdgeLabel +| AddVertexEdgeLabelProperties +| DropVertexEdgeLabelProperties + +AddVertexEdgeTables = + ('add' VertexTables)? ('add' EdgeTables)? + +DropVertexTables = + 'drop' ('vertex' | 'node') + 'tables' '(' (Name (',' Name)*) ')' + ('cascade' | 'restrict')? + +DropEdgeTables = + 'drop' ('edge' | 'relationship') + 'tables' '(' (Name (',' Name)*) ')' + ('cascade' | 'restrict')? + +AlterVertexEdgeLabels = + 'alter' ('vertex' | 'node' | 'edge' | 'relationship') 'table' Name + AddLabel AddLabel* + +AddLabel = + 'add' 'label' Name ElementTableProperties + +DropVertexEdgeLabel = + 'alter' ('vertex' | 'node' | 'edge' | 'relationship') 'table' NameRef + 'drop' 'label' NameRef ('cascade' | 'restrict')? + +AddVertexEdgeLabelProperties = + 'alter' ('vertex' | 'node' | 'edge' | 'relationship') 'table' NameRef + 'alter' 'label' NameRef + 'add' 'properties' '(' ExprAsNameList ')' + +DropVertexEdgeLabelProperties = + 'alter' ('vertex' | 'node' | 'edge' | 'relationship') 'table' NameRef + 'alter' 'label' NameRef + 'drop' 'properties' '(' (Name (',' Name)*) ')' ('cascade' | 'restrict')? + +DropPropertyGraph = + 'drop' 'property' 'graph' IfExists? Path ('cascade' | 'restrict')? + +GraphTableFn = + 'graph_table' '(' Path 'match' PathPatternList WhereClause? 'columns' '(' ExprAsNameList ')' ')' + +PathPatternList = + (PathPattern (',' PathPattern)*) + +PathPattern = + PathFactor PathFactor* + +PathFactor = + PathPrimary + GraphPatternQualifier? + +PathPrimary = + VertexPattern +| EdgeLeft +| EdgeRight +| EdgeAny +| ParenGraphPattern + +VertexPattern = + '(' Name? IsLabelExpression? WhereClause? ')' + +EdgeLeft = + '<' '-' '[' Name? IsLabelExpression? WhereClause? ']' '-' +| '<' '-' + +EdgeRight = + '-' '[' Name? IsLabelExpression? WhereClause? ']' '-' '>' +| '-' '>' + +EdgeAny = + '-' '[' Name? IsLabelExpression? WhereClause? ']' '-' +| '-' + +ParenGraphPattern = + '(' PathPattern WhereClause? ')' + +IsLabelExpression = + 'is' Expr + +GraphPatternQualifier = +// TODO: these should be integers, not Literals + '{' Literal '}' +| '{' ',' Literal '}' +| '{' Literal ',' Literal '}' + CreateAccessMethod = 'create' 'access' 'method' name:Path 'type' ('table' | 'index') @@ -3191,6 +3372,7 @@ Stmt = | AlterOperator | AlterOperatorClass | AlterOperatorFamily +| AlterPropertyGraph | AlterPolicy | AlterProcedure | AlterPublication @@ -3244,6 +3426,7 @@ Stmt = | CreateOperator | CreateOperatorClass | CreateOperatorFamily +| CreatePropertyGraph | CreatePolicy | CreateProcedure | CreatePublication @@ -3292,6 +3475,7 @@ Stmt = | DropOperatorClass | DropOperatorFamily | DropOwned +| DropPropertyGraph | DropPolicy | DropProcedure | DropPublication @@ -3331,6 +3515,7 @@ Stmt = | Notify | Prepare | PrepareTransaction +| Repack | Reassign | Refresh | Reindex diff --git a/crates/xtask/src/codegen.rs b/crates/xtask/src/codegen.rs index 112ebe6a..addd2a62 100644 --- a/crates/xtask/src/codegen.rs +++ b/crates/xtask/src/codegen.rs @@ -119,6 +119,8 @@ const PUNCT: &[(&str, &str)] = &[ (")", "R_PAREN"), ("[", "L_BRACK"), ("]", "R_BRACK"), + ("{", "L_CURLY"), + ("}", "R_CURLY"), ("<", "L_ANGLE"), (">", "R_ANGLE"), ("@", "AT"), diff --git a/crates/xtask/src/sync_regression_suite.rs b/crates/xtask/src/sync_regression_suite.rs index fd32940c..e02e30d8 100644 --- a/crates/xtask/src/sync_regression_suite.rs +++ b/crates/xtask/src/sync_regression_suite.rs @@ -40,6 +40,7 @@ const IGNORED_LINES: &[&str] = &[ ":qry;", "create table foo (with baz);", "create table foo (with ordinality);", + "SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders] COLUMNS (c.name AS customer_name)); -- error", ":show_data;", "alter trigger a on only grandparent rename to b; -- ONLY not supported", "CREATE SUBSCRIPTION regress_testsub CONNECTION 'foo';", diff --git a/postgres/kwlist.h b/postgres/kwlist.h index f0a44291..7f6ca61a 100644 --- a/postgres/kwlist.h +++ b/postgres/kwlist.h @@ -1,7 +1,7 @@ // synced from: -// commit: f95d73ed433207c4323802dc96e52f3e5553a86c -// committed at: 2026-03-05T22:43:09Z -// file: https://github.com/postgres/postgres/blob/f95d73ed433207c4323802dc96e52f3e5553a86c/src/include/parser/kwlist.h +// commit: ab697307dd0f0b4f6c6671421d4dd0bc20f176cb +// committed at: 2026-03-18T02:04:10Z +// file: https://github.com/postgres/postgres/blob/ab697307dd0f0b4f6c6671421d4dd0bc20f176cb/src/include/parser/kwlist.h // // update via: // cargo xtask sync-kwlist @@ -144,6 +144,7 @@ PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("depends", DEPENDS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("depth", DEPTH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("desc", DESC, RESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("destination", DESTINATION, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD, BARE_LABEL) @@ -155,6 +156,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("edge", EDGE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("else", ELSE, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("empty", EMPTY_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL) @@ -199,6 +201,8 @@ PG_KEYWORD("generated", GENERATED, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("global", GLOBAL, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD, AS_LABEL) PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("graph", GRAPH, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("graph_table", GRAPH_TABLE, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD, AS_LABEL) PG_KEYWORD("grouping", GROUPING, COL_NAME_KEYWORD, BARE_LABEL) @@ -305,6 +309,7 @@ PG_KEYWORD("nfd", NFD, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("nfkc", NFKC, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("nfkd", NFKD, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("no", NO, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("node", NODE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("none", NONE, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("normalize", NORMALIZE, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("normalized", NORMALIZED, UNRESERVED_KEYWORD, BARE_LABEL) @@ -369,6 +374,8 @@ PG_KEYWORD("procedural", PROCEDURAL, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("procedure", PROCEDURE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("procedures", PROCEDURES, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("properties", PROPERTIES, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("property", PROPERTY, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("quotes", QUOTES, UNRESERVED_KEYWORD, BARE_LABEL) @@ -382,9 +389,11 @@ PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("referencing", REFERENCING, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("relationship", RELATIONSHIP, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("repack", REPACK, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD, BARE_LABEL) @@ -503,6 +512,7 @@ PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD, AS_LABEL) PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("vertex", VERTEX, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("virtual", VIRTUAL, UNRESERVED_KEYWORD, BARE_LABEL) diff --git a/postgres/regression_suite/alter_generic.sql b/postgres/regression_suite/alter_generic.sql index f4efd49b..e8dde482 100644 --- a/postgres/regression_suite/alter_generic.sql +++ b/postgres/regression_suite/alter_generic.sql @@ -459,6 +459,40 @@ ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_o ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4); DROP OPERATOR FAMILY alt_opf19 USING btree; +-- +-- Property Graph +-- +SET SESSION AUTHORIZATION regress_alter_generic_user1; +CREATE PROPERTY GRAPH alt_graph1; +CREATE PROPERTY GRAPH alt_graph2; +CREATE PROPERTY GRAPH alt_graph3; + +ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph2; -- failed (name conflict) +ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph4; -- OK +ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) +ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user3; -- OK +ALTER PROPERTY GRAPH alt_graph4 SET SCHEMA alt_nsp2; -- OK +ALTER PROPERTY GRAPH alt_nsp2.alt_graph4 RENAME TO alt_graph2; -- OK +ALTER PROPERTY GRAPH alt_graph2 SET SCHEMA alt_nsp2; -- failed (name conflict) + +SET SESSION AUTHORIZATION regress_alter_generic_user2; +CREATE PROPERTY GRAPH alt_graph5; + +ALTER PROPERTY GRAPH alt_graph3 RENAME TO alt_graph5; -- failed (not owner) +ALTER PROPERTY GRAPH alt_graph5 RENAME TO alt_graph6; -- OK +ALTER PROPERTY GRAPH alt_graph3 OWNER TO regress_alter_generic_user2; -- failed (not owner) +ALTER PROPERTY GRAPH alt_graph6 OWNER TO regress_alter_generic_user3; -- failed (no role membership) +ALTER PROPERTY GRAPH alt_graph3 SET SCHEMA alt_nsp2; -- failed (not owner) + +RESET SESSION AUTHORIZATION; + +SELECT nspname, relname, rolname + FROM pg_class c, pg_namespace n, pg_authid a + WHERE c.relnamespace = n.oid AND c.relowner = a.oid + AND n.nspname in ('alt_nsp1', 'alt_nsp2') + AND c.relkind = 'g' + ORDER BY nspname, relname; + -- -- Statistics -- diff --git a/postgres/regression_suite/alter_table.sql b/postgres/regression_suite/alter_table.sql index 055bb55a..cf246bf8 100644 --- a/postgres/regression_suite/alter_table.sql +++ b/postgres/regression_suite/alter_table.sql @@ -2405,8 +2405,12 @@ CREATE TABLE nonpartitioned ( a int, b int ); -ALTER TABLE partitioned INHERIT nonpartitioned; -ALTER TABLE nonpartitioned INHERIT partitioned; +ALTER TABLE partitioned INHERIT nonpartitioned; -- fail +ALTER TABLE partitioned NO INHERIT nonpartitioned; -- fail +ALTER TABLE nonpartitioned INHERIT partitioned; -- fail +CREATE TABLE partitioned_p1 PARTITION OF partitioned FOR VALUES FROM (0, 0) TO (10, 100); +ALTER TABLE partitioned_p1 INHERIT nonpartitioned; -- fail +ALTER TABLE partitioned_p1 NO INHERIT nonpartitioned; -- fail -- cannot add NO INHERIT constraint to partitioned tables ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT; diff --git a/postgres/regression_suite/cluster.sql b/postgres/regression_suite/cluster.sql index 57ab5eb5..eb987f36 100644 --- a/postgres/regression_suite/cluster.sql +++ b/postgres/regression_suite/cluster.sql @@ -76,7 +76,6 @@ INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass ORDER BY 1; - SELECT relname, relkind, EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast FROM pg_class c WHERE relname LIKE 'clstr_tst%' ORDER BY relname; @@ -229,6 +228,26 @@ SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM o CLUSTER clstrpart; ALTER TABLE clstrpart SET WITHOUT CLUSTER; ALTER TABLE clstrpart CLUSTER ON clstrpart_idx; +-- and they cannot get an index-ordered REPACK without an explicit index name +REPACK clstrpart USING INDEX; + +-- Check that REPACK sets new relfilenodes: it should process exactly the same +-- tables as CLUSTER did. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart USING INDEX clstrpart_idx; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + +-- And finally the same for REPACK w/o index. +DROP TABLE old_cluster_info; +DROP TABLE new_cluster_info; +CREATE TEMP TABLE old_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +REPACK clstrpart; +CREATE TEMP TABLE new_cluster_info AS SELECT relname, level, relfilenode, relkind FROM pg_partition_tree('clstrpart'::regclass) AS tree JOIN pg_class c ON c.oid=tree.relid ; +SELECT relname, old.level, old.relkind, old.relfilenode = new.relfilenode FROM old_cluster_info AS old JOIN new_cluster_info AS new USING (relname) ORDER BY relname COLLATE "C"; + DROP TABLE clstrpart; -- Ownership of partitions is checked @@ -313,6 +332,57 @@ EXPLAIN (COSTS OFF) SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b; SELECT * FROM clstr_expression WHERE -a = -3 ORDER BY -a, b; COMMIT; +---------------------------------------------------------------------- +-- +-- REPACK +-- +---------------------------------------------------------------------- + +-- REPACK handles individual tables identically to CLUSTER, but it's worth +-- checking if it handles table hierarchies identically as well. +REPACK clstr_tst USING INDEX clstr_tst_c; + +-- Verify that inheritance link still works +INSERT INTO clstr_tst_inh VALUES (0, 100, 'in child table 2'); +SELECT a,b,c,substring(d for 30), length(d) from clstr_tst; + +-- Verify that foreign key link still works +INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail'); + +SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass +ORDER BY 1; + +-- Verify partial analyze works +REPACK (ANALYZE) clstr_tst (a); +REPACK (ANALYZE) clstr_tst; +REPACK (VERBOSE) clstr_tst (a); + +-- REPACK w/o argument performs no ordering, so we can only check which tables +-- have the relfilenode changed. +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_old AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); + +SET SESSION AUTHORIZATION regress_clstr_user; +SET client_min_messages = ERROR; -- order of "skipping" warnings may vary +REPACK; +RESET client_min_messages; + +RESET SESSION AUTHORIZATION; +CREATE TEMP TABLE relnodes_new AS +(SELECT relname, relfilenode +FROM pg_class +WHERE relname IN ('clstr_1', 'clstr_2', 'clstr_3')); + +-- Do the actual comparison. Unlike CLUSTER, clstr_3 should have been +-- processed because there is nothing like clustering index here. +SELECT o.relname FROM relnodes_old o +JOIN relnodes_new n ON o.relname = n.relname +WHERE o.relfilenode <> n.relfilenode +ORDER BY o.relname; + -- clean up DROP TABLE clustertest; DROP TABLE clstr_1; diff --git a/postgres/regression_suite/collate.icu.utf8.sql b/postgres/regression_suite/collate.icu.utf8.sql index 9268d49f..d0688a4d 100644 --- a/postgres/regression_suite/collate.icu.utf8.sql +++ b/postgres/regression_suite/collate.icu.utf8.sql @@ -513,6 +513,10 @@ DROP TABLE test7; CREATE COLLATION testcoll_rulesx (provider = icu, locale = '', rules = '!!wrong!!'); +-- strength specified in the rules +CREATE COLLATION strength_in_rule (provider = icu, locale = 'und', deterministic = false, rules = '[strength 1]'); +SELECT 'a' = 'à' COLLATE strength_in_rule; -- true because of the rule + -- nondeterministic collations diff --git a/postgres/regression_suite/constraints.sql b/postgres/regression_suite/constraints.sql index f1746042..b9169ffd 100644 --- a/postgres/regression_suite/constraints.sql +++ b/postgres/regression_suite/constraints.sql @@ -266,6 +266,58 @@ COPY COPY_TBL FROM 'filename'; SELECT * FROM COPY_TBL; +-- +-- CHECK constraints +-- ALTER TABLE ALTER CONSTRAINT [NOT] ENFORCED +create table parted_ch( + a int, b int, + constraint cc check (a > 10) not enforced, + constraint cc_1 check (b < 17) not enforced +) partition by range(a); +create table parted_ch_1 partition of parted_ch for values from (0) to (10) partition by list(b); +create table parted_ch_11 partition of parted_ch_1 for values in (0, 1, 22,4); +create table parted_ch_12 partition of parted_ch_1 for values in (2); +create table parted_ch_2(b int, a int, + constraint cc check (a > 10) not enforced, + constraint cc_1 check (b < 17) enforced, + constraint cc_2 check( a < 15) not enforced +); +alter table parted_ch attach partition parted_ch_2 for values from (10) to (20); + +insert into parted_ch values (1, 22), (9, 1), (16, 16); + +alter table parted_ch alter constraint cc_1 enforced; --error +update parted_ch set b = 4 where b = 22; +alter table parted_ch alter constraint cc_1 enforced; --ok + +create or replace view check_constraint_status as +select conname, conrelid::regclass, conenforced, convalidated +from pg_constraint +where conrelid::regclass::text ~* '^parted_ch' and contype = 'c' +order by conname, conrelid::regclass::text collate "C"; + +alter table parted_ch alter constraint cc not enforced; --no-op +alter table parted_ch alter constraint cc enforced; --error +delete from parted_ch where a = 1; +alter table parted_ch alter constraint cc enforced; --error +delete from parted_ch where a = 9; +alter table parted_ch alter constraint cc enforced; + +--check these CHECK constraint status +select * from check_constraint_status; + +alter table parted_ch_2 alter constraint cc_2 enforced; --error +delete from parted_ch where a = 16; +alter table parted_ch_2 alter constraint cc_2 enforced; +alter table parted_ch_2 alter constraint cc not enforced; +alter table parted_ch_2 alter constraint cc_1 not enforced; +alter table parted_ch_2 alter constraint cc_2 not enforced; + +--check these CHECK constraint status again +select * from check_constraint_status; +drop table parted_ch; +drop view check_constraint_status; + -- -- Primary keys -- diff --git a/postgres/regression_suite/create_property_graph.sql b/postgres/regression_suite/create_property_graph.sql new file mode 100644 index 00000000..13edc13f --- /dev/null +++ b/postgres/regression_suite/create_property_graph.sql @@ -0,0 +1,365 @@ +CREATE SCHEMA create_property_graph_tests; +GRANT USAGE ON SCHEMA create_property_graph_tests TO PUBLIC; +SET search_path = create_property_graph_tests; +CREATE SCHEMA create_property_graph_tests_2; +GRANT USAGE ON SCHEMA create_property_graph_tests_2 TO PUBLIC; + +CREATE ROLE regress_graph_user1; +CREATE ROLE regress_graph_user2; + +CREATE PROPERTY GRAPH g1; + +COMMENT ON PROPERTY GRAPH g1 IS 'a graph'; + +CREATE PROPERTY GRAPH g1; -- error: duplicate + +CREATE TABLE t1 (a int, b text); +CREATE TABLE t2 (i int PRIMARY KEY, j int, k int); +CREATE TABLE t3 (x int, y text, z text); + +CREATE TABLE e1 (a int, i int, t text, PRIMARY KEY (a, i)); +CREATE TABLE e2 (a int, x int, t text); + +CREATE PROPERTY GRAPH g2 + VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL, t3 KEY (x) LABEL t3l1 LABEL t3l2) + EDGE TABLES ( + e1 + SOURCE KEY (a) REFERENCES t1 (a) + DESTINATION KEY (i) REFERENCES t2 (i), + e2 KEY (a, x) + SOURCE KEY (a) REFERENCES t1 (a) + DESTINATION KEY (x, t) REFERENCES t3 (x, y) + ); + +-- test dependencies/object descriptions + +DROP TABLE t1; -- fail +ALTER TABLE t1 DROP COLUMN b; -- non-key column; fail +ALTER TABLE t1 DROP COLUMN a; -- key column; fail + +-- like g2 but assembled with ALTER +CREATE PROPERTY GRAPH g3; +ALTER PROPERTY GRAPH g3 ADD VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL); +ALTER PROPERTY GRAPH g3 + ADD VERTEX TABLES (t3 KEY (x) LABEL t3l1) + ADD EDGE TABLES ( + e1 SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (i) REFERENCES t2 (i), + e2 KEY (a, x) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (x, t) REFERENCES t3 (x, y) + ); +ALTER PROPERTY GRAPH g3 + ALTER VERTEX TABLE t3 + ADD LABEL t3l2 PROPERTIES ALL COLUMNS + ADD LABEL t3l3 PROPERTIES ALL COLUMNS; +ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3x; -- error +ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3; +ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2); -- fail +ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2) CASCADE; +ALTER PROPERTY GRAPH g3 DROP EDGE TABLES (e2); + +CREATE PROPERTY GRAPH g4 + VERTEX TABLES ( + t1 KEY (a) NO PROPERTIES, + t2 DEFAULT LABEL PROPERTIES (i + j AS i_j, k), + t3 KEY (x) LABEL t3l1 PROPERTIES (x, y AS yy) LABEL t3l2 PROPERTIES (x, z AS zz) + ) + EDGE TABLES ( + e1 + SOURCE KEY (a) REFERENCES t1 (a) + DESTINATION KEY (i) REFERENCES t2 (i) + PROPERTIES ALL COLUMNS, + e2 KEY (a, x) + SOURCE KEY (a) REFERENCES t1 (a) + DESTINATION KEY (x, t) REFERENCES t3 (x, y) + PROPERTIES ALL COLUMNS + ); + +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS kk); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 DROP PROPERTIES (k); + +CREATE TABLE t11 (a int PRIMARY KEY); +CREATE TABLE t12 (b int PRIMARY KEY); +CREATE TABLE t13 ( + c int PRIMARY KEY, + d int REFERENCES t11, + e int REFERENCES t12 +); + +CREATE PROPERTY GRAPH g5 + VERTEX TABLES (t11, t12) + EDGE TABLES (t13 SOURCE t11 DESTINATION t12); + +SELECT pg_get_propgraphdef('g5'::regclass); + +-- error cases +CREATE UNLOGGED PROPERTY GRAPH gx VERTEX TABLES (xx, yy); +CREATE PROPERTY GRAPH gx VERTEX TABLES (xx, yy); +CREATE PROPERTY GRAPH gx VERTEX TABLES (t1 KEY (a), t2 KEY (i), t1 KEY (a)); +ALTER PROPERTY GRAPH g3 ADD VERTEX TABLES (t3 KEY (x)); -- duplicate alias +CREATE PROPERTY GRAPH gx + VERTEX TABLES (t1 AS tt KEY (a), t2 KEY (i)) + EDGE TABLES ( + e1 SOURCE t1 DESTINATION t2 + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES (t1 KEY (a), t2 KEY (i)) + EDGE TABLES ( + e1 SOURCE t1 DESTINATION tx + ); +COMMENT ON PROPERTY GRAPH gx IS 'not a graph'; +CREATE PROPERTY GRAPH gx + VERTEX TABLES (t1 KEY (a), t2) + EDGE TABLES ( + e1 SOURCE t1 DESTINATION t2 -- no foreign keys + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) + LABEL foo PROPERTIES (a + 1 AS aa) + LABEL bar PROPERTIES (1 + a AS aa) -- expression mismatch + ); +ALTER PROPERTY GRAPH g2 + ADD VERTEX TABLES ( + t1 AS t1x KEY (a) + LABEL foo PROPERTIES (a + 1 AS aa) + LABEL bar PROPERTIES (1 + a AS aa) -- expression mismatch + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) PROPERTIES (b AS p1), + t2 PROPERTIES (k AS p1) -- type mismatch + ); +ALTER PROPERTY GRAPH g2 ALTER VERTEX TABLE t1 ADD LABEL foo PROPERTIES (b AS k); -- type mismatch + +CREATE TABLE t1x (a int, b varchar(10)); +CREATE TABLE t2x (i int, j varchar(15)); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1x KEY (a) PROPERTIES (b AS p1), + t2x KEY (i) PROPERTIES (j AS p1) -- typmod mismatch + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1x KEY (a) PROPERTIES (b::varchar(20) AS p1), + t2x KEY (i) PROPERTIES (j::varchar(25) AS p1) -- typmod mismatch + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1x KEY (a) PROPERTIES (b::varchar(20) AS p1), + t2x KEY (i) PROPERTIES (j::varchar(20) AS p1) -- matching typmods by casting works + ); +DROP PROPERTY GRAPH gx; +DROP TABLE t1x, t2x; + +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a, a AS aa), + t2 KEY (i) LABEL l1 PROPERTIES (i AS a, j AS b, k) -- mismatching number of properties on label + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a, b), + t2 KEY (i) LABEL l1 PROPERTIES (i AS a) -- mismatching number of properties on label + ); +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a, b), + t2 KEY (i) LABEL l1 PROPERTIES (i AS a, j AS j) -- mismatching property names on label + ); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t1 ADD LABEL t3l1 PROPERTIES (a AS x, b AS yy, b AS zz); -- mismatching number of properties on label +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t1 ADD LABEL t3l1 PROPERTIES (a AS x, b AS zz); -- mismatching property names on label +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t1 ADD LABEL t3l1 PROPERTIES (a AS x); -- mismatching number of properties on label + +ALTER PROPERTY GRAPH g1 OWNER TO regress_graph_user1; +SET ROLE regress_graph_user1; +GRANT SELECT ON PROPERTY GRAPH g1 TO regress_graph_user2; +GRANT UPDATE ON PROPERTY GRAPH g1 TO regress_graph_user2; -- fail +RESET ROLE; + +-- collation + +CREATE TABLE tc1 (a int, b text); +CREATE TABLE tc2 (a int, b text); +CREATE TABLE tc3 (a int, b text COLLATE "C"); + +CREATE TABLE ec1 (ek1 int, ek2 int, eb text); +CREATE TABLE ec2 (ek1 int, ek2 int, eb text COLLATE "POSIX"); + +CREATE PROPERTY GRAPH gc1 + VERTEX TABLES (tc1 KEY (a), tc2 KEY (a), tc3 KEY (a)); -- fail +CREATE PROPERTY GRAPH gc1 + VERTEX TABLES (tc1 KEY (a), tc2 KEY (a)) + EDGE TABLES ( + ec1 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a), + ec2 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + ); -- fail +CREATE PROPERTY GRAPH gc1 + VERTEX TABLES (tc1 KEY (a) DEFAULT LABEL PROPERTIES (a), tc3 KEY (b)) + EDGE TABLES ( + ec2 KEY (ek1, eb) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (eb) REFERENCES tc3 (b) + ); -- fail +CREATE PROPERTY GRAPH gc1 + VERTEX TABLES (tc1 KEY (a), tc2 KEY (a)) + EDGE TABLES ( + ec1 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + ); +ALTER PROPERTY GRAPH gc1 ADD VERTEX TABLES (tc3 KEY (a)); -- fail +ALTER PROPERTY GRAPH gc1 + ADD EDGE TABLES ( + ec2 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + ); -- fail +ALTER PROPERTY GRAPH gc1 + ADD VERTEX TABLES ( + tc3 KEY (a) DEFAULT LABEL PROPERTIES (a, b COLLATE pg_catalog.DEFAULT AS b) + ); +ALTER PROPERTY GRAPH gc1 + ADD EDGE TABLES ( + ec2 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + DEFAULT LABEL PROPERTIES (ek1, ek2, eb COLLATE pg_catalog.DEFAULT AS eb) + ); +DROP PROPERTY GRAPH gc1; +CREATE PROPERTY GRAPH gc1 + VERTEX TABLES ( + tc1 KEY (a) DEFAULT LABEL PROPERTIES (a, b::varchar COLLATE "C" AS b), + tc2 KEY (a) DEFAULT LABEL PROPERTIES (a, (b COLLATE "C")::varchar AS b), + tc3 KEY (a) DEFAULT LABEL PROPERTIES (a, b::varchar AS b) + ) + EDGE TABLES ( + ec1 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + DEFAULT LABEL PROPERTIES (ek1, ek2, eb), + ec2 KEY (ek1, ek2) + SOURCE KEY (ek1) REFERENCES tc1 (a) + DESTINATION KEY (ek2) REFERENCES tc2 (a) + DEFAULT LABEL PROPERTIES (ek1, ek2, eb COLLATE pg_catalog.DEFAULT AS eb) + ); + +-- type inconsistency check + +CREATE TABLE v1 (a int primary key, b text); +CREATE TABLE e(k1 text, k2 text, c text); +CREATE TABLE v2 (m text, n text); +CREATE PROPERTY GRAPH gt + VERTEX TABLES (v1 KEY (a), v2 KEY (m)) + EDGE TABLES ( + e KEY (k1, k2) + SOURCE KEY (k1) REFERENCES v1(a) + DESTINATION KEY (k2) REFERENCES v2(m) + ); -- fail +ALTER TABLE e DROP COLUMN k1, ADD COLUMN k1 bigint primary key; +CREATE PROPERTY GRAPH gt + VERTEX TABLES (v1 KEY (a), v2 KEY (m)) + EDGE TABLES ( + e KEY (k1, k2) + SOURCE KEY (k1) REFERENCES v1(a) + DESTINATION KEY (k2) REFERENCES v2(m) + ); + +-- information schema + +SELECT * FROM information_schema.property_graphs ORDER BY property_graph_name; +SELECT * FROM information_schema.pg_element_tables ORDER BY property_graph_name, element_table_alias; +SELECT * FROM information_schema.pg_element_table_key_columns ORDER BY property_graph_name, element_table_alias, ordinal_position; +SELECT * FROM information_schema.pg_edge_table_components ORDER BY property_graph_name, edge_table_alias, edge_end DESC, ordinal_position; +SELECT * FROM information_schema.pg_element_table_labels ORDER BY property_graph_name, element_table_alias, label_name; +SELECT * FROM information_schema.pg_element_table_properties ORDER BY property_graph_name, element_table_alias, property_name; +SELECT * FROM information_schema.pg_label_properties ORDER BY property_graph_name, label_name, property_name; +SELECT * FROM information_schema.pg_labels ORDER BY property_graph_name, label_name; +SELECT * FROM information_schema.pg_property_data_types ORDER BY property_graph_name, property_name; +SELECT * FROM information_schema.pg_property_graph_privileges WHERE grantee LIKE 'regress%' ORDER BY property_graph_name, grantor, grantee, privilege_type; + +-- test object address functions +SELECT pg_describe_object(classid, objid, objsubid) as obj, + pg_describe_object(refclassid, refobjid, refobjsubid) as reference_graph + FROM pg_depend + WHERE refclassid = 'pg_class'::regclass AND + refobjid = 'create_property_graph_tests.g2'::regclass + ORDER BY 1, 2; +SELECT (pg_identify_object_as_address(classid, objid, objsubid)).* + FROM pg_depend + WHERE refclassid = 'pg_class'::regclass AND + refobjid = 'create_property_graph_tests.g2'::regclass + ORDER BY 1, 2, 3; +SELECT (pg_identify_object(classid, objid, objsubid)).* + FROM pg_depend + WHERE refclassid = 'pg_class'::regclass AND + refobjid = 'create_property_graph_tests.g2'::regclass + ORDER BY 1, 2, 3, 4; + +-- \a\t +SELECT pg_get_propgraphdef('g2'::regclass); +SELECT pg_get_propgraphdef('g3'::regclass); +SELECT pg_get_propgraphdef('g4'::regclass); + +SELECT pg_get_propgraphdef('pg_type'::regclass); -- error +-- \a\t + +-- Test \d variants for property graphs +-- \dG g1 +-- \dG+ g1 +-- \dGx g1 +-- \d g2 +-- \d g1 +-- \d+ g2 +-- \d+ g1 +-- \dG g_nonexistent +-- \dG t11 +-- \set QUIET 'off' +-- \dG g_nonexistent +-- \set QUIET 'on' + +-- temporary property graph + +-- Keep this at the end to avoid test failure due to changing temporary +-- namespace names in information schema query outputs +CREATE TEMPORARY PROPERTY GRAPH g1; -- same name as persistent graph +DROP PROPERTY GRAPH g1; -- drops temporary graph retaining persistent graph +-- \dG g1 +CREATE TEMPORARY TABLE v2tmp (m text, n text); +CREATE TEMPORARY PROPERTY GRAPH gtmp + VERTEX TABLES (v1 KEY (a), v2tmp KEY (m)) + EDGE TABLES ( + e KEY (k1, k2) + SOURCE KEY (k1) REFERENCES v1(a) + DESTINATION KEY (k2) REFERENCES v2tmp(m) + ); +DROP PROPERTY GRAPH gtmp; +CREATE PROPERTY GRAPH gtmp + VERTEX TABLES (v1 KEY (a), v2tmp KEY (m)) + EDGE TABLES ( + e KEY (k1, k2) + SOURCE KEY (k1) REFERENCES v1(a) + DESTINATION KEY (k2) REFERENCES v2tmp(m) + ); +ALTER PROPERTY GRAPH g1 + ADD VERTEX TABLES (v2tmp KEY (m)); -- error + + +-- DROP, ALTER SET SCHEMA, ALTER PROPERTY GRAPH RENAME TO + +DROP TABLE g2; -- error: wrong object type +CREATE VIEW vg1 AS SELECT * FROM GRAPH_TABLE(g1 MATCH () COLUMNS (1 AS one)); +DROP PROPERTY GRAPH g1; -- error +ALTER PROPERTY GRAPH g1 SET SCHEMA create_property_graph_tests_2; +ALTER PROPERTY GRAPH create_property_graph_tests_2.g1 RENAME TO g2; +DROP PROPERTY GRAPH create_property_graph_tests_2.g2 CASCADE; +DROP PROPERTY GRAPH g1; -- error +ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (t1 KEY (a)); -- error +ALTER PROPERTY GRAPH IF EXISTS g1 SET SCHEMA create_property_graph_tests_2; +DROP PROPERTY GRAPH IF EXISTS g1; + +DROP ROLE regress_graph_user1, regress_graph_user2; + +-- leave remaining objects behind for pg_upgrade/pg_dump tests diff --git a/postgres/regression_suite/fast_default.sql b/postgres/regression_suite/fast_default.sql index 068dd0bc..8ff29cf2 100644 --- a/postgres/regression_suite/fast_default.sql +++ b/postgres/regression_suite/fast_default.sql @@ -287,11 +287,62 @@ ORDER BY attnum; SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2; +-- test fast default over domains with constraints +CREATE DOMAIN domain5 AS int CHECK(value > 10) DEFAULT 8; +CREATE DOMAIN domain6 as int CHECK(value > 10) DEFAULT random(min=>11, max=>100); +CREATE DOMAIN domain7 as int CHECK((value + random(min=>11::int, max=>11)) > 12); +CREATE DOMAIN domain8 as int NOT NULL; + +CREATE TABLE test_add_domain_col(a int); +-- succeeds despite constraint-violating default because table is empty +ALTER TABLE test_add_domain_col ADD COLUMN a1 domain5; +ALTER TABLE test_add_domain_col DROP COLUMN a1; +INSERT INTO test_add_domain_col VALUES(1),(2); + +-- tests with non-empty table +ALTER TABLE test_add_domain_col ADD COLUMN b domain5; -- table rewrite, then fail +ALTER TABLE test_add_domain_col ADD COLUMN b domain8; -- table rewrite, then fail +ALTER TABLE test_add_domain_col ADD COLUMN b domain5 DEFAULT 1; -- table rewrite, then fail +ALTER TABLE test_add_domain_col ADD COLUMN b domain5 DEFAULT 12; -- ok, no table rewrite + +-- explicit column default expression overrides domain's default +-- expression, so no table rewrite +ALTER TABLE test_add_domain_col ADD COLUMN c domain6 DEFAULT 14; + +ALTER TABLE test_add_domain_col ADD COLUMN c1 domain8 DEFAULT 13; -- no table rewrite +SELECT attnum, attname, atthasmissing, atthasdef, attmissingval +FROM pg_attribute +WHERE attnum > 0 AND attrelid = 'test_add_domain_col'::regclass AND attisdropped is false +AND atthasmissing +ORDER BY attnum; + +-- We need to rewrite the table whenever domain default contains volatile expression +ALTER TABLE test_add_domain_col ADD COLUMN d domain6; + +-- We need to rewrite the table whenever domain constraint expression contains volatile expression +ALTER TABLE test_add_domain_col ADD COLUMN e domain7 default 14; +ALTER TABLE test_add_domain_col ADD COLUMN f domain7; + +-- domain with both volatile and non-volatile CHECK constraints: the +-- volatile one forces a table rewrite +CREATE DOMAIN domain9 AS int CHECK(value > 10) CHECK((value + random(min=>1::int, max=>1)) > 0); +ALTER TABLE test_add_domain_col ADD COLUMN g domain9 DEFAULT 14; + +-- virtual generated columns cannot have domain types +ALTER TABLE test_add_domain_col ADD COLUMN h domain5 + GENERATED ALWAYS AS (a + 20) VIRTUAL; -- error + DROP TABLE t2; +DROP TABLE test_add_domain_col; DROP DOMAIN domain1; DROP DOMAIN domain2; DROP DOMAIN domain3; DROP DOMAIN domain4; +DROP DOMAIN domain5; +DROP DOMAIN domain6; +DROP DOMAIN domain7; +DROP DOMAIN domain8; +DROP DOMAIN domain9; DROP FUNCTION foo(INT); -- Fall back to full rewrite for volatile expressions diff --git a/postgres/regression_suite/foreign_data.sql b/postgres/regression_suite/foreign_data.sql index 99e33aed..82832661 100644 --- a/postgres/regression_suite/foreign_data.sql +++ b/postgres/regression_suite/foreign_data.sql @@ -67,6 +67,11 @@ CREATE FUNCTION invalid_fdw_handler() RETURNS int LANGUAGE SQL AS 'SELECT 1;'; CREATE FOREIGN DATA WRAPPER test_fdw HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler; + +-- should preserve dependency on test_fdw_handler +ALTER FOREIGN DATA WRAPPER test_fdw VALIDATOR postgresql_fdw_validator; +DROP FUNCTION test_fdw_handler(); -- ERROR + DROP FOREIGN DATA WRAPPER test_fdw; -- ALTER FOREIGN DATA WRAPPER @@ -383,10 +388,12 @@ COMMENT ON COLUMN ft1.c1 IS NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer; +ALTER FOREIGN TABLE ft1 ADD COLUMN IF NOT EXISTS c6 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); +ALTER FOREIGN TABLE ft1 ADD c11 integer; ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; @@ -419,6 +426,7 @@ ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE ft1 DROP COLUMN c9; +ALTER FOREIGN TABLE ft1 DROP c11; ALTER FOREIGN TABLE ft1 ADD COLUMN c11 serial; ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR @@ -430,10 +438,12 @@ ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1; -- alter noexisting table ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c6 integer; +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN IF NOT EXISTS c6 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD c11 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 DROP NOT NULL; @@ -447,8 +457,10 @@ ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT IF EXISTS no_cons ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT ft1_c1_check; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN c9; +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP c11; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1; diff --git a/postgres/regression_suite/graph_table.sql b/postgres/regression_suite/graph_table.sql new file mode 100644 index 00000000..de9319cc --- /dev/null +++ b/postgres/regression_suite/graph_table.sql @@ -0,0 +1,567 @@ +CREATE SCHEMA graph_table_tests; +GRANT USAGE ON SCHEMA graph_table_tests TO PUBLIC; +SET search_path = graph_table_tests; + +CREATE TABLE products ( + product_no integer PRIMARY KEY, + name varchar, + price numeric +); + +CREATE TABLE customers ( + customer_id integer PRIMARY KEY, + name varchar, + address varchar +); + +CREATE TABLE orders ( + order_id integer PRIMARY KEY, + ordered_when date +); + +CREATE TABLE order_items ( + order_items_id integer PRIMARY KEY, + order_id integer REFERENCES orders (order_id), + product_no integer REFERENCES products (product_no), + quantity integer +); + +CREATE TABLE customer_orders ( + customer_orders_id integer PRIMARY KEY, + customer_id integer REFERENCES customers (customer_id), + order_id integer REFERENCES orders (order_id) +); + +CREATE TABLE wishlists ( + wishlist_id integer PRIMARY KEY, + wishlist_name varchar +); + +CREATE TABLE wishlist_items ( + wishlist_items_id integer PRIMARY KEY, + wishlist_id integer REFERENCES wishlists (wishlist_id), + product_no integer REFERENCES products (product_no) +); + +CREATE TABLE customer_wishlists ( + customer_wishlist_id integer PRIMARY KEY, + customer_id integer REFERENCES customers (customer_id), + wishlist_id integer REFERENCES wishlists (wishlist_id) +); + +CREATE PROPERTY GRAPH myshop + VERTEX TABLES ( + products, + customers, + orders + DEFAULT LABEL + LABEL lists PROPERTIES (order_id AS node_id, 'order'::varchar(10) AS list_type), + wishlists + DEFAULT LABEL + LABEL lists PROPERTIES (wishlist_id AS node_id, 'wishlist'::varchar(10) AS list_type) + ) + EDGE TABLES ( + order_items KEY (order_items_id) + SOURCE KEY (order_id) REFERENCES orders (order_id) + DESTINATION KEY (product_no) REFERENCES products (product_no) + DEFAULT LABEL + LABEL list_items PROPERTIES (order_id AS link_id, product_no), + wishlist_items KEY (wishlist_items_id) + SOURCE KEY (wishlist_id) REFERENCES wishlists (wishlist_id) + DESTINATION KEY (product_no) REFERENCES products (product_no) + DEFAULT LABEL + LABEL list_items PROPERTIES (wishlist_id AS link_id, product_no), + customer_orders KEY (customer_orders_id) + SOURCE KEY (customer_id) REFERENCES customers (customer_id) + DESTINATION KEY (order_id) REFERENCES orders (order_id) + DEFAULT LABEL + LABEL cust_lists PROPERTIES (customer_id, order_id AS link_id), + customer_wishlists KEY (customer_wishlist_id) + SOURCE KEY (customer_id) REFERENCES customers (customer_id) + DESTINATION KEY (wishlist_id) REFERENCES wishlists (wishlist_id) + DEFAULT LABEL + LABEL cust_lists PROPERTIES (customer_id, wishlist_id AS link_id) + ); + +SELECT customer_name FROM GRAPH_TABLE (xxx MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error +SELECT customer_name FROM GRAPH_TABLE (pg_class MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error +SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (cx.name AS customer_name)); -- error +SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.namex AS customer_name)); -- error +SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers|employees WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error +-- SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders] COLUMNS (c.name AS customer_name)); -- error +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers), (o IS orders) COLUMNS (c.name AS customer_name)); -- error +SELECT * FROM GRAPH_TABLE (myshop MATCH COLUMNS (1 AS col)); -- error, empty match clause +SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers)->{1,2}(o IS orders) COLUMNS (c.name AS customer_name)); -- error +SELECT * FROM GRAPH_TABLE (myshop MATCH ((c IS customers)->(o IS orders)) COLUMNS (c.name)); + +-- a property graph can be referenced only from within GRAPH_TABLE clause. +SELECT * FROM myshop; -- error +COPY myshop TO stdout; -- error +INSERT INTO myshop VALUES (1); -- error + +INSERT INTO products VALUES + (1, 'product1', 10), + (2, 'product2', 20), + (3, 'product3', 30); +INSERT INTO customers VALUES + (1, 'customer1', 'US'), + (2, 'customer2', 'CA'), + (3, 'customer3', 'GL'); +INSERT INTO orders VALUES + (1, date '2024-01-01'), + (2, date '2024-01-02'), + (3, date '2024-01-03'); +INSERT INTO wishlists VALUES + (1, 'wishlist1'), + (2, 'wishlist2'), + (3, 'wishlist3'); +INSERT INTO order_items (order_items_id, order_id, product_no, quantity) VALUES + (1, 1, 1, 5), + (2, 1, 2, 10), + (3, 2, 1, 7); +INSERT INTO customer_orders (customer_orders_id, customer_id, order_id) VALUES + (1, 1, 1), + (2, 2, 2); +INSERT INTO customer_wishlists (customer_wishlist_id, customer_id, wishlist_id) VALUES + (1, 2, 3), + (2, 3, 1), + (3, 3, 2); +INSERT INTO wishlist_items (wishlist_items_id, wishlist_id, product_no) VALUES + (1, 1, 2), + (2, 1, 3), + (3, 2, 1), + (4, 3, 1); + +-- single element path pattern +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (c.name)); +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name)); +-- graph element specification without label or variable +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[]->(o IS orders) COLUMNS (c.name AS customer_name)); +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[co IS customer_orders]->(o IS orders WHERE o.ordered_when = date '2024-01-02') COLUMNS (c.name, c.address)); +SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)-[IS customer_orders]->(c IS customers) COLUMNS (c.name, o.ordered_when)); +SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)<-[IS customer_orders]-(c IS customers) COLUMNS (c.name, o.ordered_when)); +-- spaces around pattern operators +SELECT * FROM GRAPH_TABLE (myshop MATCH ( o IS orders ) <- [ IS customer_orders ] - (c IS customers) COLUMNS ( c.name, o.ordered_when)); +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS cust_lists]->(l IS lists)-[ IS list_items]->(p IS products) COLUMNS (c.name AS customer_name, p.name AS product_name, l.list_type)) ORDER BY customer_name, product_name, list_type; +-- label disjunction +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS customer_orders | customer_wishlists ]->(l IS orders | wishlists)-[ IS list_items]->(p IS products) COLUMNS (c.name AS customer_name, p.name AS product_name)) ORDER BY customer_name, product_name; +-- property not associated with labels queried results in error +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS customer_orders | customer_wishlists ]->(l IS orders | wishlists)-[ IS list_items]->(p IS products) COLUMNS (c.name AS customer_name, p.name AS product_name, l.list_type)) ORDER BY 1, 2, 3; +-- vertex to vertex connection abbreviation +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)->(o IS orders) COLUMNS (c.name, o.ordered_when)) ORDER BY 1; + +-- lateral test +CREATE TABLE x1 (a int, b text); +INSERT INTO x1 VALUES (1, 'one'), (2, 'two'); +SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US' AND c.customer_id = x1.a)-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name, c.customer_id AS cid)); +DROP TABLE x1; + +CREATE TABLE v1 ( + id int PRIMARY KEY, + vname varchar(10), + vprop1 int, + vprop2 int +); + +CREATE TABLE v2 ( + id1 int, + id2 int, + vname varchar(10), + vprop1 int, + vprop2 int +); + +CREATE TABLE v3 ( + id int PRIMARY KEY, + vname varchar(10), + vprop1 int, + vprop2 int +); + +-- edge connecting v1 and v2 +CREATE TABLE e1_2 ( + id_1 int, + id_2_1 int, + id_2_2 int, + ename varchar(10), + eprop1 int +); + +-- edge connecting v1 and v3 +CREATE TABLE e1_3 ( + id_1 int, + id_3 int, + ename varchar(10), + eprop1 int, + PRIMARY KEY (id_1, id_3) +); + +CREATE TABLE e2_3 ( + id_2_1 int, + id_2_2 int, + id_3 int, + ename varchar(10), + eprop1 int +); + +CREATE PROPERTY GRAPH g1 + VERTEX TABLES ( + v1 + LABEL vl1 PROPERTIES (vname, vprop1) + LABEL l1 PROPERTIES (vname AS elname), -- label shared by vertexes as well as edges + v2 KEY (id1, id2) + LABEL vl2 PROPERTIES (vname, vprop2, 'vl2_prop'::varchar(10) AS lprop1) + LABEL vl3 PROPERTIES (vname, vprop1, 'vl2_prop'::varchar(10) AS lprop1) + LABEL l1 PROPERTIES (vname AS elname), + v3 + LABEL vl3 PROPERTIES (vname, vprop1, 'vl3_prop'::varchar(10) AS lprop1) + LABEL l1 PROPERTIES (vname AS elname) + ) + -- edges with differing number of columns in destination keys + EDGE TABLES ( + e1_2 key (id_1, id_2_1, id_2_2) + SOURCE KEY (id_1) REFERENCES v1 (id) + DESTINATION KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + LABEL el1 PROPERTIES (eprop1, ename) + LABEL l1 PROPERTIES (ename AS elname), + e1_3 + SOURCE KEY (id_1) REFERENCES v1 (id) + DESTINATION KEY (id_3) REFERENCES v3 (id) + -- order of property names doesn't matter + LABEL el1 PROPERTIES (ename, eprop1) + LABEL l1 PROPERTIES (ename AS elname), + e2_3 key (id_2_1, id_2_2, id_3) + SOURCE KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + DESTINATION KEY (id_3) REFERENCES v3 (id) + -- new property lprop2 not shared by el1 + -- does not share eprop1 from by el1 + LABEL el2 PROPERTIES (ename, eprop1 * 10 AS lprop2) + LABEL l1 PROPERTIES (ename AS elname) + ); + +INSERT INTO v1 VALUES + (1, 'v11', 10, 100), + (2, 'v12', 20, 200), + (3, 'v13', 30, 300); +INSERT INTO v2 VALUES + (1000, 1, 'v21', 1010, 1100), + (1000, 2, 'v22', 1020, 1200), + (1000, 3, 'v23', 1030, 1300); +INSERT INTO v3 VALUES + (2001, 'v31', 2010, 2100), + (2002, 'v32', 2020, 2200), + (2003, 'v33', 2030, 2300); +INSERT INTO e1_2 VALUES + (1, 1000, 2, 'e121', 10001), + (2, 1000, 1, 'e122', 10002); +INSERT INTO e1_3 VALUES + (1, 2003, 'e131', 10003), + (1, 2001, 'e132', 10004); +INSERT INTO e2_3 VALUES (1000, 2, 2002, 'e231', 10005); + +-- empty element path pattern, counts number of edges in the graph +SELECT count(*) FROM GRAPH_TABLE (g1 MATCH ()-[]->() COLUMNS (1 AS one)); +SELECT count(*) FROM GRAPH_TABLE (g1 MATCH ()->() COLUMNS (1 AS one)); +-- Project property associated with a label specified in the graph pattern even +-- if it is defined for a graph element through a different label. (Refer +-- section 6.5 of SQL/PGQ standard). For example, vprop1 in the query below. It +-- is defined on v2 through label vl3, but gets exposed in the query through +-- label vl1 which is not associated with v2. v2, in turn, is included because +-- of label vl2. +SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2) COLUMNS (a.vname, a.vprop1)); +-- vprop2 is associated with vl2 but not vl3 +SELECT src, conn, dest, lprop1, vprop2, vprop1 FROM GRAPH_TABLE (g1 MATCH (a IS vl1)-[b IS el1]->(c IS vl2 | vl3) COLUMNS (a.vname AS src, b.ename AS conn, c.vname AS dest, c.lprop1, c.vprop2, c.vprop1)); +-- edges directed in both ways - to and from v2 +SELECT * FROM GRAPH_TABLE (g1 MATCH (v1 IS vl2)-[conn]-(v2) COLUMNS (v1.vname AS v1name, conn.ename AS cname, v2.vname AS v2name)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (v1 IS vl2)-(v2) COLUMNS (v1.vname AS v1name, v2.vname AS v2name)); + +-- Errors +-- vl1 is not associated with property vprop2 +SELECT src, src_vprop2, conn, dest FROM GRAPH_TABLE (g1 MATCH (a IS vl1)-[b IS el1]->(c IS vl2 | vl3) COLUMNS (a.vname AS src, a.vprop2 AS src_vprop2, b.ename AS conn, c.vname AS dest)); +-- property ename is associated with edge labels but not with a vertex label +SELECT * FROM GRAPH_TABLE (g1 MATCH (src)-[conn]->(dest) COLUMNS (src.vname AS svname, src.ename AS sename)); +-- vname is associated vertex labels but not with an edge label +SELECT * FROM GRAPH_TABLE (g1 MATCH (src)-[conn]->(dest) COLUMNS (conn.vname AS cvname, conn.ename AS cename)); +-- el1 is associated with only edges, and cannot qualify a vertex +SELECT * FROM GRAPH_TABLE (g1 MATCH (src IS el1)-[conn]->(dest) COLUMNS (conn.ename AS cename)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (src IS el1 | vl1)-[conn]->(dest) COLUMNS (conn.ename AS cename)); +-- star in COLUMNs is specified but not supported +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.*)); +-- star anywhere else is not allowed as a property reference +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT NULL)-[IS customer_orders]->(o IS orders) COLUMNS (c.name)); + +-- select all the properties across all the labels associated with a given type +-- of graph element +SELECT * FROM GRAPH_TABLE (g1 MATCH (src)-[conn]->(dest) COLUMNS (src.vname AS svname, conn.ename AS cename, dest.vname AS dvname, src.vprop1 AS svp1, src.vprop2 AS svp2, src.lprop1 AS slp1, dest.vprop1 AS dvp1, dest.vprop2 AS dvp2, dest.lprop1 AS dlp1, conn.eprop1 AS cep1, conn.lprop2 AS clp2)); +-- three label disjunction +SELECT * FROM GRAPH_TABLE (g1 MATCH (src IS vl1 | vl2 | vl3)-[conn]->(dest) COLUMNS (src.vname AS svname, conn.ename AS cename, dest.vname AS dvname)); +-- graph'ical query: find a vertex which is not connected to any other vertex as a source or a destination. +WITH all_connected_vertices AS (SELECT svn, dvn FROM GRAPH_TABLE (g1 MATCH (src)-[conn]->(dest) COLUMNS (src.vname AS svn, dest.vname AS dvn))), + all_vertices AS (SELECT vn FROM GRAPH_TABLE (g1 MATCH (vertex) COLUMNS (vertex.vname AS vn))) +SELECT vn FROM all_vertices EXCEPT (SELECT svn FROM all_connected_vertices UNION SELECT dvn FROM all_connected_vertices) ORDER BY vn; +-- query all connections using a label shared by vertices and edges +SELECT sn, cn, dn FROM GRAPH_TABLE (g1 MATCH (src IS l1)-[conn IS l1]->(dest IS l1) COLUMNS (src.elname AS sn, conn.elname AS cn, dest.elname AS dn)); + +-- Tests for cyclic path patterns +CREATE TABLE e2_1 ( + id_2_1 int, + id_2_2 int, + id_1 int, + ename varchar(10), + eprop1 int +); + +CREATE TABLE e3_2 ( + id_3 int, + id_2_1 int, + id_2_2 int, + ename varchar(10), + eprop1 int +); + +ALTER PROPERTY GRAPH g1 + ADD EDGE TABLES ( + e2_1 KEY (id_2_1, id_2_2, id_1) + SOURCE KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + DESTINATION KEY (id_1) REFERENCES v1 (id) + LABEL el1 PROPERTIES (eprop1, ename) + LABEL l1 PROPERTIES (ename AS elname), + e3_2 KEY (id_3, id_2_1, id_2_2) + SOURCE KEY (id_3) REFERENCES v3 (id) + DESTINATION KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + LABEL el2 PROPERTIES (ename, eprop1 * 10 AS lprop2) + LABEL l1 PROPERTIES (ename AS elname) + ); + +INSERT INTO e1_2 VALUES (3, 1000, 3, 'e123', 10007); +INSERT INTO e2_1 VALUES (1000, 1, 2, 'e211', 10006); +INSERT INTO e2_1 VALUES (1000, 3, 3, 'e212', 10008); +INSERT INTO e3_2 VALUES (2002, 1000, 2, 'e321', 10009); + +-- cyclic pattern using WHERE clause in graph pattern, +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)->(b)->(c) WHERE a.vname = c.vname COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +-- cyclic pattern using element patterns with the same variable name +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)->(b)->(a) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +-- cyclic pattern with WHERE clauses in element patterns with the same variable name +SELECT * FROM GRAPH_TABLE (g1 MATCH (a WHERE a.vprop1 < 2000)->(b WHERE b.vprop1 > 20)->(a WHERE a.vprop1 > 20) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)->(b WHERE b.vprop1 > 20)->(a WHERE a.vprop1 between 20 and 2000) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +SELECT * FROM GRAPH_TABLE (g1 MATCH (a WHERE a.vprop1 between 20 and 2000)->(b WHERE b.vprop1 > 20)->(a WHERE a.vprop1 between 20 and 2000) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +-- labels and elements kinds of element patterns with the same variable name +SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS l1)-[a IS l1]->(b IS l1) COLUMNS (a.ename AS aename, b.ename AS bename)) ORDER BY 1, 2; -- error +SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1)->(b)->(a IS vl2) WHERE a.vname <> b.vname COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; -- error +SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1)->(b)->(a) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)->(b)->(a IS vl1) COLUMNS (a.vname AS self, b.vname AS through, a.vprop1 AS self_p1, b.vprop1 AS through_p1)) ORDER BY self, through; + +-- add loop to test edge patterns with same variable name +CREATE TABLE e3_3 ( + src_id int, + dest_id int, + ename varchar(10), + eprop1 int +); + +ALTER PROPERTY GRAPH g1 + ADD EDGE TABLES ( + e3_3 KEY (src_id, dest_id) + SOURCE KEY (src_id) REFERENCES v3 (id) + DESTINATION KEY (dest_id) REFERENCES v3 (id) + LABEL el2 PROPERTIES (ename, eprop1 * 10 AS lprop2) + LABEL l1 PROPERTIES (ename AS elname) + ); + +INSERT INTO e3_3 VALUES (2003, 2003, 'e331', 10010); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b]->(a)-[b]->(a) COLUMNS (a.vname AS self, b.ename AS loop_name)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b]->(c)-[b]->(d) COLUMNS (a.vname AS aname, b.ename AS bname, c.vname AS cname, d.vname AS dname)); --error +-- the looping edge should be reported only once even when edge pattern with any direction is used +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[c]-(a) COLUMNS (a.vname AS self, c.ename AS loop_name)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-(a) COLUMNS (a.vname AS self)); + +-- test collation specified in the expression +INSERT INTO e3_3 VALUES (2003, 2003, 'E331', 10011); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b]->(a)-[b]->(a) COLUMNS (a.vname AS self, b.ename AS loop_name)) ORDER BY loop_name COLLATE "C" ASC; +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b IS el2 WHERE b.ename > 'E331' COLLATE "C"]->(a)-[b]->(a) COLUMNS (a.vname AS self, b.ename AS loop_name)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b]->(a)-[b]->(a) WHERE b.ename > 'E331' COLLATE "C" COLUMNS (a.vname AS self, b.ename AS loop_name)); +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b]->(a)-[b]->(a) COLUMNS (a.vname AS self, b.ename AS loop_name)) WHERE loop_name > 'E331' COLLATE "C"; + +-- property graph with some of the elements, labels and properties same as the +-- previous one. Test whether components from the specified property graph are +-- used. Also test explicit collation specification in property. +CREATE PROPERTY GRAPH g2 + VERTEX TABLES ( + v1 + LABEL l1 PROPERTIES ('g2.' || vname COLLATE "C" AS elname), + v2 KEY (id1, id2) + LABEL l1 PROPERTIES ('g2.' || vname COLLATE "C" AS elname), + v3 + LABEL l1 PROPERTIES ('g2.' || vname COLLATE "C" AS elname) + ) + EDGE TABLES ( + e1_2 key (id_1, id_2_1, id_2_2) + SOURCE KEY (id_1) REFERENCES v1 (id) + DESTINATION KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + LABEL l1 PROPERTIES ('g2.' || ename COLLATE "C" AS elname), + e1_3 + SOURCE KEY (id_1) REFERENCES v1 (id) + DESTINATION KEY (id_3) REFERENCES v3 (id) + LABEL l1 PROPERTIES ('g2.' || ename COLLATE "C" AS elname), + e2_3 KEY (id_2_1, id_2_2, id_3) + SOURCE KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + DESTINATION KEY (id_3) REFERENCES v3 (id) + LABEL l1 PROPERTIES ('g2.' || ename COLLATE "C" AS elname), + e3_3 KEY (src_id, dest_id) + SOURCE KEY (src_id) REFERENCES v3 (id) + DESTINATION KEY (src_id) REFERENCES v3 (id) + LABEL l1 PROPERTIES ('g2.' || ename COLLATE "C" AS elname) + ); +SELECT sn, cn, dn FROM GRAPH_TABLE (g2 MATCH (src IS l1)-[conn IS l1]->(dest IS l1) COLUMNS (src.elname AS sn, conn.elname AS cn, dest.elname AS dn)) ORDER BY 1, 2, 3; +SELECT * FROM GRAPH_TABLE (g2 MATCH (a)-[b WHERE b.elname > 'g2.E331']->(a)-[b]->(a) COLUMNS (a.elname AS self, b.elname AS loop_name)); +SELECT * FROM GRAPH_TABLE (g2 MATCH (a)-[b]->(a)-[b]->(a) WHERE b.elname > 'g2.E331' COLUMNS (a.elname AS self, b.elname AS loop_name)); +SELECT * FROM GRAPH_TABLE (g2 MATCH (a)-[b]->(a)-[b]->(a) COLUMNS (a.elname AS self, b.elname AS loop_name)) WHERE loop_name > 'g2.E331'; + +-- prepared statements, any changes to the property graph should be reflected in +-- the already prepared statements +PREPARE cyclestmt AS SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS l1)->(b IS l1)->(c IS l1) WHERE a.elname = c.elname COLUMNS (a.elname AS self, b.elname AS through)) ORDER BY self, through; +EXECUTE cyclestmt; +ALTER PROPERTY GRAPH g1 DROP EDGE TABLES (e3_2, e3_3); +EXECUTE cyclestmt; +ALTER PROPERTY GRAPH g1 + ADD EDGE TABLES ( + e3_2 KEY (id_3, id_2_1, id_2_2) + SOURCE KEY (id_3) REFERENCES v3 (id) + DESTINATION KEY (id_2_1, id_2_2) REFERENCES v2 (id1, id2) + LABEL el2 PROPERTIES (ename, eprop1 * 10 AS lprop2) + LABEL l1 PROPERTIES (ename AS elname) + ); +EXECUTE cyclestmt; +ALTER PROPERTY GRAPH g1 ALTER VERTEX TABLE v3 DROP LABEL l1; +EXECUTE cyclestmt; +ALTER PROPERTY GRAPH g1 ALTER VERTEX TABLE v3 ADD LABEL l1 PROPERTIES (vname AS elname); +EXECUTE cyclestmt; +ALTER PROPERTY GRAPH g1 + ADD EDGE TABLES ( + e3_3 KEY (src_id, dest_id) + SOURCE KEY (src_id) REFERENCES v3 (id) + DESTINATION KEY (src_id) REFERENCES v3 (id) + LABEL l2 PROPERTIES (ename AS elname) + ); +PREPARE loopstmt AS SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[e IS l2]->(a) COLUMNS (e.elname AS loop)) ORDER BY loop COLLATE "C" ASC; +EXECUTE loopstmt; +ALTER PROPERTY GRAPH g1 ALTER EDGE TABLE e3_3 ALTER LABEL l2 DROP PROPERTIES (elname); +EXECUTE loopstmt; -- error +ALTER PROPERTY GRAPH g1 ALTER EDGE TABLE e3_3 ALTER LABEL l2 ADD PROPERTIES ((ename || '_new')::varchar(10) AS elname); +EXECUTE loopstmt; + +-- inheritance and partitioning +CREATE TABLE pv (id int, val int); +CREATE TABLE cv1 () INHERITS (pv); +CREATE TABLE cv2 () INHERITS (pv); +INSERT INTO pv VALUES (1, 10); +INSERT INTO cv1 VALUES (2, 20); +INSERT INTO cv2 VALUES (3, 30); +CREATE TABLE pe (id int, src int, dest int, val int); +CREATE TABLE ce1 () INHERITS (pe); +CREATE TABLE ce2 () INHERITS (pe); +INSERT INTO pe VALUES (1, 1, 2, 100); +INSERT INTO ce1 VALUES (2, 2, 3, 200); +INSERT INTO ce2 VALUES (3, 3, 1, 300); +CREATE PROPERTY GRAPH g3 + NODE TABLES ( + pv KEY (id) + ) + RELATIONSHIP TABLES ( + pe KEY (id) + SOURCE KEY(src) REFERENCES pv(id) + DESTINATION KEY(dest) REFERENCES pv(id) + ); +SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe]->(d IS pv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3; +-- temporary property graph +CREATE TEMPORARY PROPERTY GRAPH gtmp + VERTEX TABLES ( + pv KEY (id) + ) + EDGE TABLES ( + pe KEY (id) + SOURCE KEY(src) REFERENCES pv(id) + DESTINATION KEY(dest) REFERENCES pv(id) + ); +SELECT * FROM GRAPH_TABLE (gtmp MATCH (s IS pv)-[e IS pe]->(d IS pv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3; + +CREATE TABLE ptnv (id int PRIMARY KEY, val int) PARTITION BY LIST(id); +CREATE TABLE prtv1 PARTITION OF ptnv FOR VALUES IN (1, 2); +CREATE TABLE prtv2 PARTITION OF ptnv FOR VALUES IN (3); +INSERT INTO ptnv VALUES (1, 10), (2, 20), (3, 30); +CREATE TABLE ptne (id int PRIMARY KEY, src int REFERENCES ptnv(id), dest int REFERENCES ptnv(id), val int) PARTITION BY LIST(id); +CREATE TABLE ptne1 PARTITION OF ptne FOR VALUES IN (1, 2); +CREATE TABLE ptne2 PARTITION OF ptne FOR VALUES IN (3); +INSERT INTO ptne VALUES (1, 1, 2, 100), (2, 2, 3, 200), (3, 3, 1, 300); +CREATE PROPERTY GRAPH g4 + VERTEX TABLES (ptnv) + EDGE TABLES ( + ptne + SOURCE KEY (src) REFERENCES ptnv(id) + DESTINATION KEY (dest) REFERENCES ptnv(id) + ); +SELECT * FROM GRAPH_TABLE (g4 MATCH (s IS ptnv)-[e IS ptne]->(d IS ptnv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3; +-- edges from the same vertex in both directions connecting to other vertexes in the same table +SELECT * FROM GRAPH_TABLE (g4 MATCH (s)-[e]-(d) WHERE s.id = 3 COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3; +SELECT * FROM GRAPH_TABLE (g4 MATCH (s WHERE s.id = 3)-[e]-(d) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3; + +-- ruleutils reverse parsing +CREATE VIEW customers_us AS SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders | customer_wishlists ]->(l IS orders | wishlists)-[ IS list_items]->(p IS products) COLUMNS (c.name AS customer_name, p.name AS product_name)) ORDER BY customer_name, product_name; +SELECT pg_get_viewdef('customers_us'::regclass); + +-- test view/graph nesting + +CREATE VIEW customers_view AS SELECT customer_id, 'redacted' || customer_id AS name_redacted, address FROM customers; +SELECT * FROM customers; +SELECT * FROM customers_view; + +CREATE PROPERTY GRAPH myshop2 + VERTEX TABLES ( + products, + customers_view KEY (customer_id) LABEL customers, + orders + ) + EDGE TABLES ( + order_items KEY (order_items_id) + SOURCE KEY (order_id) REFERENCES orders (order_id) + DESTINATION KEY (product_no) REFERENCES products (product_no), + customer_orders KEY (customer_orders_id) + SOURCE KEY (customer_id) REFERENCES customers_view (customer_id) + DESTINATION KEY (order_id) REFERENCES orders (order_id) + ); + +CREATE VIEW customers_us_redacted AS SELECT * FROM GRAPH_TABLE (myshop2 MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name_redacted AS customer_name_redacted)); + +SELECT * FROM customers_us_redacted; + +-- GRAPH_TABLE in UDFs +CREATE FUNCTION out_degree(sname varchar) RETURNS varchar AS $$ +DECLARE + out_degree int; +BEGIN + SELECT count(*) INTO out_degree FROM GRAPH_TABLE (g1 MATCH (src WHERE src.vname = sname)->() COLUMNS (src.vname)); + RETURN out_degree; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION direct_connections(sname varchar) +RETURNS TABLE (cname varchar, dname varchar) +AS $$ + SELECT cname, dname FROM GRAPH_TABLE (g1 MATCH (src WHERE src.vname = sname)-[conn]->(dst) COLUMNS (conn.ename AS cname, dst.vname AS dname)); +$$ LANGUAGE SQL; + +SELECT sname, out_degree(sname) FROM GRAPH_TABLE (g1 MATCH (src IS vl1) COLUMNS (src.vname AS sname)); +SELECT sname, cname, dname FROM GRAPH_TABLE (g1 MATCH (src IS vl1) COLUMNS (src.vname AS sname)), LATERAL direct_connections(sname); + +-- GRAPH_TABLE joined to a regular table +SELECT * FROM customers co, GRAPH_TABLE (myshop2 MATCH (cg IS customers WHERE cg.address = co.address)-[IS customer_orders]->(o IS orders) COLUMNS (cg.name_redacted AS customer_name_redacted)) WHERE co.customer_id = 1; + +-- graph table in a subquery +SELECT * FROM customers co WHERE co.customer_id = (SELECT customer_id FROM GRAPH_TABLE (myshop2 MATCH (cg IS customers WHERE cg.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (cg.customer_id))); + +-- query within graph table +SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE src.vprop1 > (SELECT max(v1.vprop1) FROM v1) COLUMNS(src.vname AS sname, dest.vname AS dname)); +SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE out_degree(src.vname) > (SELECT max(out_degree(nname)) FROM GRAPH_TABLE (g1 MATCH (node) COLUMNS (node.vname AS nname))) COLUMNS(src.vname AS sname, dest.vname AS dname)); + +-- leave the objects behind for pg_upgrade/pg_dump tests diff --git a/postgres/regression_suite/graph_table_rls.sql b/postgres/regression_suite/graph_table_rls.sql new file mode 100644 index 00000000..044bc27c --- /dev/null +++ b/postgres/regression_suite/graph_table_rls.sql @@ -0,0 +1,363 @@ +-- +--Test RLS with GRAPH_TABLE +-- +--This test verifies that Row Level Security (RLS) policies are correctly +--enforced when querying tables underlying property graphs using GRAPH_TABLE. +--graph_table.sql has extensive tests covering interaction of GRAPH_TABLE with +--other query constructs. rowsecurity.sql has extensive coverage of interaction +--of RLS and other features of PostgreSQL. This test along with those two tests +--is sufficient to make sure that all combinations of RLS and GRAPH_TABLE will +--work as expected. + +-- Clean up in case a prior regression run failed + +-- Suppress NOTICE messages when users/groups don't exist +SET client_min_messages TO 'warning'; + +DROP USER IF EXISTS regress_graph_rls_alice; +DROP USER IF EXISTS regress_graph_rls_bob; +DROP USER IF EXISTS regress_graph_rls_carol; +DROP USER IF EXISTS regress_graph_rls_dave; +DROP USER IF EXISTS regress_graph_rls_exempt_user; +DROP ROLE IF EXISTS regress_graph_rls_group1; +DROP ROLE IF EXISTS regress_graph_rls_group2; + +DROP SCHEMA IF EXISTS graph_rls_schema CASCADE; + +RESET client_min_messages; + +-- initial setup +CREATE USER regress_graph_rls_alice NOLOGIN; +CREATE USER regress_graph_rls_bob NOLOGIN; +CREATE USER regress_graph_rls_carol NOLOGIN; +CREATE USER regress_graph_rls_dave NOLOGIN; +CREATE USER regress_graph_rls_exempt_user BYPASSRLS NOLOGIN; +CREATE ROLE regress_graph_rls_group1 NOLOGIN; +CREATE ROLE regress_graph_rls_group2 NOLOGIN; + +GRANT regress_graph_rls_group1 TO regress_graph_rls_dave; +GRANT regress_graph_rls_group2 TO regress_graph_rls_bob; + +CREATE SCHEMA graph_rls_schema; +GRANT ALL ON SCHEMA graph_rls_schema to public; +SET search_path = graph_rls_schema; + +-- setup for leaky-function tests +CREATE FUNCTION f_leak(text) RETURNS bool + COST 0.0000001 LANGUAGE plpgsql + AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END'; + +SET SESSION AUTHORIZATION regress_graph_rls_alice; + +CREATE TABLE users (uid int PRIMARY KEY, pguser name, seclv int); +INSERT INTO users VALUES + (1, 'regress_graph_rls_alice', 99), + (2, 'regress_graph_rls_bob', 1), + (3, 'regress_graph_rls_carol', 2), + (4, 'regress_graph_rls_dave', 3); +GRANT SELECT ON users TO public; + +CREATE TABLE document_people ( + did int, + dlevel int, + dtitle text); +INSERT INTO document_people VALUES + ( 1, 2, 'Politicians'), + ( 2, 3, 'Artists'), + ( 3, 1, 'Scientists'), + ( 4, 100, 'Unspeakables'); +GRANT SELECT ON document_people TO public; + +CREATE TABLE accessed ( + aid int, + uid int, + did int); +INSERT INTO accessed VALUES + (1, 2, 3), + (2, 3, 1), + (3, 3, 3), + (4, 4, 3), + (5, 1, 1), + (6, 1, 4), + (7, 4, 2); +GRANT SELECT ON accessed TO public; +CREATE PROPERTY GRAPH cabinet + VERTEX TABLES (users KEY (uid), + document_people AS document KEY (did)) + EDGE TABLES (accessed KEY (aid) + SOURCE KEY (uid) REFERENCES users (uid) + DESTINATION KEY (did) REFERENCES document (did)); +GRANT SELECT ON cabinet TO public; + +-- +-- Basic RLS tests +-- +ALTER TABLE document_people ENABLE ROW LEVEL SECURITY; +-- user's security level must be higher than or equal to document's +CREATE POLICY p1 ON document_people AS PERMISSIVE + USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user)); +-- but Dave isn't allowed to see document titled 'Scientists' +CREATE POLICY p2 ON document_people AS RESTRICTIVE TO regress_graph_rls_dave + USING (dtitle <> 'Scientists'); +CREATE POLICY p3 ON document_people AS RESTRICTIVE TO regress_graph_rls_group1 + USING (dlevel < 3); +CREATE POLICY p4 ON document_people AS PERMISSIVE TO regress_graph_rls_group2 + USING (dlevel < 3); + +SET row_security TO ON; + +-- Use the same query in all the test cases below. Prepare it once and +-- use multiple times. Apart from making the test file shorter and avoiding +-- duplication, it also tests that a prepared statement correctly reflect changes +-- to RLS policies, session user or RLS settings. +PREPARE graph_rls_query AS +SELECT * FROM GRAPH_TABLE (cabinet + MATCH (u IS users)-[a IS accessed]->(d IS document) + WHERE f_leak(d.dtitle) + COLUMNS (u.pguser, a.aid, d.dtitle, d.dlevel)) + ORDER BY 1, 2, 3, 4; + +-- viewpoint from regress_graph_rls_bob +SET SESSION AUTHORIZATION regress_graph_rls_bob; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_carol +SET SESSION AUTHORIZATION regress_graph_rls_carol; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_dave +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS enabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +SET row_security TO ON; +EXECUTE graph_rls_query; + +SET row_security TO OFF; +-- database superuser does bypass RLS policy when disabled +RESET SESSION AUTHORIZATION; +EXECUTE graph_rls_query; + +-- database non-superuser with bypass privilege can bypass RLS policy when disabled +SET SESSION AUTHORIZATION regress_graph_rls_exempt_user; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS disabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +EXECUTE graph_rls_query; + +-- When RLS disabled, other users get ERROR. +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; -- error +SET SESSION AUTHORIZATION regress_graph_rls_alice; +DROP PROPERTY GRAPH cabinet; + +-- +-- Table inheritance +-- +ALTER TABLE document_people ADD COLUMN category text DEFAULT 'People'; +CREATE TABLE document_places ( + did int, + dlevel int, + dtitle text, + category text DEFAULT 'Places'); +INSERT INTO document_places VALUES + ( 5, 1, 'Paris'), + ( 6, 2, 'Tokyo'), + ( 7, 3, 'New York'); +GRANT SELECT ON document_places TO public; +-- Setup inheritance +CREATE TABLE document ( + did int, + dlevel int, + dtitle text); +GRANT SELECT ON document TO public; +ALTER TABLE document_people INHERIT document; +ALTER TABLE document_places INHERIT document; +INSERT INTO accessed VALUES + (11, 2, 5), + (12, 3, 6), + (13, 1, 7), + (14, 4, 5), + (15, 1, 6); + +-- Enable RLS and move policies p1 and p2 to parent table but leave p3 and p4 on +-- child table. The policies on child table are not applied when querying parent +-- table. +ALTER TABLE document ENABLE ROW LEVEL SECURITY; +DROP POLICY p1 ON document_people; +DROP POLICY p2 ON document_people; +CREATE POLICY p1 ON document AS PERMISSIVE + USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user)); +CREATE POLICY p2 ON document AS RESTRICTIVE TO regress_graph_rls_dave + USING (dtitle <> 'Scientists'); + +CREATE PROPERTY GRAPH cabinet + VERTEX TABLES (users KEY (uid), document KEY (did)) + EDGE TABLES (accessed KEY (aid) + SOURCE KEY (uid) REFERENCES users (uid) + DESTINATION KEY (did) REFERENCES document (did)); +GRANT SELECT ON cabinet TO public; + +SET row_security TO ON; + +-- viewpoint from regress_graph_rls_bob +SET SESSION AUTHORIZATION regress_graph_rls_bob; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_carol +SET SESSION AUTHORIZATION regress_graph_rls_carol; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_dave +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS enabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +SET row_security TO ON; +EXECUTE graph_rls_query; + +SET row_security TO OFF; +-- database superuser does bypass RLS policy when disabled +RESET SESSION AUTHORIZATION; +EXECUTE graph_rls_query; + +-- database non-superuser with bypass privilege can bypass RLS policy when disabled +SET SESSION AUTHORIZATION regress_graph_rls_exempt_user; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS disabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +EXECUTE graph_rls_query; + +-- When RLS disabled, other users get ERROR. +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; -- error + +-- cleanup +SET SESSION AUTHORIZATION regress_graph_rls_alice; +DROP PROPERTY GRAPH cabinet; +ALTER TABLE document_people NO INHERIT document; +ALTER TABLE document_places NO INHERIT document; +DROP TABLE document; + +-- +-- Partitioned Tables +-- +CREATE TABLE document ( + did int, + dlevel int, + dtitle text, + category text) PARTITION BY LIST (category); +GRANT SELECT ON document TO public; +ALTER TABLE document ATTACH PARTITION document_people FOR VALUES IN ('People'); +ALTER TABLE document ATTACH PARTITION document_places FOR VALUES IN ('Places'); +-- Enable RLS on partitioned table +ALTER TABLE document ENABLE ROW LEVEL SECURITY; +-- create policies on partitioned table +CREATE POLICY p1 ON document AS PERMISSIVE + USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user)); +CREATE POLICY p2 ON document AS RESTRICTIVE TO regress_graph_rls_dave + USING (dtitle <> 'Scientists'); + +CREATE PROPERTY GRAPH cabinet + VERTEX TABLES (users KEY (uid), document KEY (did)) + EDGE TABLES (accessed KEY (aid) + SOURCE KEY (uid) REFERENCES users (uid) + DESTINATION KEY (did) REFERENCES document (did)); +GRANT SELECT ON cabinet TO public; +SET row_security TO ON; + +-- viewpoint from regress_graph_rls_bob +SET SESSION AUTHORIZATION regress_graph_rls_bob; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_carol +SET SESSION AUTHORIZATION regress_graph_rls_carol; +EXECUTE graph_rls_query; + +-- viewpoint from regress_graph_rls_dave +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS enabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +SET row_security TO ON; +EXECUTE graph_rls_query; + +SET row_security TO OFF; +-- database superuser does bypass RLS policy when disabled +RESET SESSION AUTHORIZATION; +EXECUTE graph_rls_query; + +-- database non-superuser with bypass privilege can bypass RLS policy when disabled +SET SESSION AUTHORIZATION regress_graph_rls_exempt_user; +EXECUTE graph_rls_query; + +-- RLS policy does not apply to table owner when RLS disabled +SET SESSION AUTHORIZATION regress_graph_rls_alice; +EXECUTE graph_rls_query; + +-- When RLS disabled, other users get ERROR. +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; -- error + +-- +-- Recursion through GRAPH_TABLE also throws error +-- +SET SESSION AUTHORIZATION regress_graph_rls_alice; +SET row_security TO ON; +-- Create a policy on document that references document itself via GRAPH_TABLE +CREATE POLICY pr ON document TO regress_graph_rls_dave + USING (EXISTS (SELECT 1 FROM GRAPH_TABLE (cabinet + MATCH (u IS users)-[a IS accessed]->(d IS document) + WHERE u.pguser = current_user + COLUMNS (a.aid)))); +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; -- error +SET SESSION AUTHORIZATION regress_graph_rls_alice; +DROP POLICY pr ON document; + +-- +-- Command specific policy. Since GRAPH_TABLE can be used in only SELECT, test +-- only FOR SELECT policies. +-- +DROP POLICY p1 ON document; +DROP POLICY p2 ON document; +CREATE POLICY p1 ON document AS PERMISSIVE + FOR SELECT + USING (dlevel <= (SELECT seclv FROM users WHERE pguser = current_user)); +CREATE POLICY p2 ON document AS RESTRICTIVE + FOR SELECT TO regress_graph_rls_dave + USING (dtitle <> 'Scientists'); +SET SESSION AUTHORIZATION regress_graph_rls_dave; +EXECUTE graph_rls_query; + +-- +-- Default deny policy, FORCE ROW LEVEL SECURITY +-- +SET SESSION AUTHORIZATION regress_graph_rls_alice; +DROP POLICY p1 ON document; +DROP POLICY p2 ON document; +-- default deny policy applies to non-owners, non-rls-exempt and non-superusers +SET SESSION AUTHORIZATION regress_graph_rls_bob; +EXECUTE graph_rls_query; +-- Deny RLS policy does not apply to table owner, superuser or RLS exempt user +SET SESSION AUTHORIZATION regress_graph_rls_exempt_user; +EXECUTE graph_rls_query; +RESET SESSION AUTHORIZATION; +EXECUTE graph_rls_query; +SET SESSION AUTHORIZATION regress_graph_rls_alice; +EXECUTE graph_rls_query; +-- FORCE ROW LEVEL SECURITY applies RLS to owners too +ALTER TABLE document FORCE ROW LEVEL SECURITY; +EXECUTE graph_rls_query; +SET row_security TO OFF; +EXECUTE graph_rls_query; -- error + +-- Clean up +DEALLOCATE graph_rls_query; + +-- leave objects behind for pg_upgrade/pg_dump tests diff --git a/postgres/regression_suite/inherit.sql b/postgres/regression_suite/inherit.sql index c1c150a5..105db59d 100644 --- a/postgres/regression_suite/inherit.sql +++ b/postgres/regression_suite/inherit.sql @@ -510,6 +510,38 @@ select conrelid::regclass::text as relname, conname, conislocal, coninhcount, co from pg_constraint where conname like 'inh\_check\_constraint%' order by 1, 2; +-- +-- CHECK constraints +-- ALTER TABLE ALTER CONSTRAINT [NOT] ENFORCED +alter table p1 drop constraint inh_check_constraint1; +alter table p1_c1 drop constraint inh_check_constraint1; + +alter table only p1 alter constraint inh_check_constraint3 enforced; --error +alter table only p1 alter constraint inh_check_constraint3 not enforced; --error + +insert into p1_c1 values(-2); +insert into p1_c3 values(-3); + +alter table p1 alter constraint inh_check_constraint3 enforced; --error +delete from only p1_c1 where f1 = -2; +alter table p1_c1 alter constraint inh_check_constraint3 enforced; --error + +delete from only p1_c3 where f1 = -3; +alter table p1 alter constraint inh_check_constraint3 enforced; --ok +alter table p1 alter constraint inh_check_constraint3 not enforced; --ok +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'inh_check_constraint3' and contype = 'c' +order by conrelid::regclass::text collate "C"; +drop table p1 cascade; + +--for "no inherit" check constraint, it will not recurse to child table +create table p1(f1 int constraint p1_a_check check (f1 > 0) no inherit not enforced); +create table p1_c1(f1 int constraint p1_a_check check (f1 > 0) not enforced); +alter table p1_c1 inherit p1; +insert into p1_c1 values(-11); +alter table p1 alter constraint p1_a_check enforced; --ok +alter table p1_c1 alter constraint p1_a_check enforced; --error drop table p1 cascade; -- @@ -520,6 +552,17 @@ drop table p1 cascade; create table p1(f1 int constraint p1_a_check check (f1 > 0) not enforced); create table p1_c1(f1 int constraint p1_a_check check (f1 > 0) enforced); alter table p1_c1 inherit p1; +insert into p1 values(-1); --ok +insert into p1_c1 values(-1); --error +alter table p1 alter constraint p1_a_check enforced; --error +truncate p1; +alter table p1 alter constraint p1_a_check enforced; --ok +alter table p1 alter constraint p1_a_check not enforced; --ok + +select conname, conenforced, convalidated, conrelid::regclass +from pg_constraint +where conname = 'p1_a_check' and contype = 'c' +order by conrelid::regclass::text collate "C"; drop table p1 cascade; create table p1(f1 int constraint p1_a_check check (f1 > 0) enforced); diff --git a/postgres/regression_suite/join.sql b/postgres/regression_suite/join.sql index ad90c326..7f3449e2 100644 --- a/postgres/regression_suite/join.sql +++ b/postgres/regression_suite/join.sql @@ -1726,12 +1726,12 @@ order by fault; explain (costs off) select * from (values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x +left join (values (1, 10), (2, 20), (2, null)) as v2(v2x,v2y) on v2x = v1x left join unnest(v1ys) as u1(u1y) on u1y = v2y; select * from (values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x +left join (values (1, 10), (2, 20), (2, null)) as v2(v2x,v2y) on v2x = v1x left join unnest(v1ys) as u1(u1y) on u1y = v2y; -- diff --git a/postgres/regression_suite/join_hash.sql b/postgres/regression_suite/join_hash.sql index 49d3fd61..989390e6 100644 --- a/postgres/regression_suite/join_hash.sql +++ b/postgres/regression_suite/join_hash.sql @@ -57,6 +57,7 @@ $$; -- estimated size. create table simple as select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +insert into simple values (null, null); alter table simple set (parallel_workers = 2); analyze simple; @@ -83,8 +84,8 @@ update pg_class set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 where relname = 'extremely_skewed'; --- Make a relation with a couple of enormous tuples. -create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; +-- Make a relation with several enormous tuples. +create table wide as select generate_series(1, 3) as id, rpad('', 320000, 'x') as t; alter table wide set (parallel_workers = 2); -- The "optimal" case: the hash table fits in memory; we plan for 1 @@ -496,14 +497,14 @@ set work_mem = '128kB'; set hash_mem_multiplier = 1.0; explain (costs off) select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); + from wide left join (select id, coalesce(t, '') || '' as t from wide where id < 3) s using (id); select length(max(s.t)) -from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); +from wide left join (select id, coalesce(t, '') || '' as t from wide where id < 3) s using (id); select final > 1 as multibatch from hash_join_batches( $$ select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); + from wide left join (select id, coalesce(t, '') || '' as t from wide where id < 3) s using (id); $$); rollback to settings; diff --git a/postgres/regression_suite/misc_functions.sql b/postgres/regression_suite/misc_functions.sql index d40c5ccc..e85e469f 100644 --- a/postgres/regression_suite/misc_functions.sql +++ b/postgres/regression_suite/misc_functions.sql @@ -4,47 +4,6 @@ -- \set regresslib :libdir '/regress' :dlsuffix --- Function to assist with verifying EXPLAIN which includes costs. A series --- of bool flags allows control over which portions are masked out -CREATE FUNCTION explain_mask_costs(query text, do_analyze bool, - hide_costs bool, hide_row_est bool, hide_width bool) RETURNS setof text -LANGUAGE plpgsql AS -$$ -DECLARE - ln text; - analyze_str text; -BEGIN - IF do_analyze = true THEN - analyze_str := 'on'; - ELSE - analyze_str := 'off'; - END IF; - - -- avoid jit related output by disabling it - SET LOCAL jit = 0; - - FOR ln IN - EXECUTE format('explain (analyze %s, costs on, summary off, timing off, buffers off) %s', - analyze_str, query) - LOOP - IF hide_costs = true THEN - ln := regexp_replace(ln, 'cost=\d+\.\d\d\.\.\d+\.\d\d', 'cost=N..N'); - END IF; - - IF hide_row_est = true THEN - -- don't use 'g' so that we leave the actual rows intact - ln := regexp_replace(ln, 'rows=\d+', 'rows=N'); - END IF; - - IF hide_width = true THEN - ln := regexp_replace(ln, 'width=\d+', 'width=N'); - END IF; - - RETURN NEXT ln; - END LOOP; -END; -$$; - -- -- num_nulls() -- @@ -277,89 +236,6 @@ SELECT * FROM tenk1 a JOIN my_gen_series(1,1000) g ON a.unique1 = g; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 a JOIN my_gen_series(1,10) g ON a.unique1 = g; --- --- Test the SupportRequestRows support function for generate_series_timestamp() --- - --- Ensure the row estimate matches the actual rows -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day') g(s);$$, -true, true, false, true); - --- As above but with generate_series_timestamp -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMP '2024-02-01', TIMESTAMP '2024-03-01', INTERVAL '1 day') g(s);$$, -true, true, false, true); - --- As above but with generate_series_timestamptz_at_zone() -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day', 'UTC') g(s);$$, -true, true, false, true); - --- Ensure the estimated and actual row counts match when the range isn't --- evenly divisible by the step -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '7 day') g(s);$$, -true, true, false, true); - --- Ensure the estimates match when step is decreasing -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '-1 day') g(s);$$, -true, true, false, true); - --- Ensure an empty range estimates 1 row -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '1 day') g(s);$$, -true, true, false, true); - --- Ensure we get the default row estimate for infinity values -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(TIMESTAMPTZ '-infinity', TIMESTAMPTZ 'infinity', INTERVAL '1 day') g(s);$$, -false, true, false, true); - --- Ensure the row estimate behaves correctly when step size is zero. --- We expect generate_series_timestamp() to throw the error rather than in --- the support function. -SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '0 day') g(s); - --- --- Test the SupportRequestRows support function for generate_series_numeric() --- - --- Ensure the row estimate matches the actual rows -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(1.0, 25.0) g(s);$$, -true, true, false, true); - --- As above but with non-default step -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(1.0, 25.0, 2.0) g(s);$$, -true, true, false, true); - --- Ensure the estimates match when step is decreasing -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(25.0, 1.0, -1.0) g(s);$$, -true, true, false, true); - --- Ensure an empty range estimates 1 row -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(25.0, 1.0, 1.0) g(s);$$, -true, true, false, true); - --- Ensure we get the default row estimate for error cases (infinity/NaN values --- and zero step size) -SELECT explain_mask_costs($$ -SELECT * FROM generate_series('-infinity'::NUMERIC, 'infinity'::NUMERIC, 1.0) g(s);$$, -false, true, false, true); - -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(1.0, 25.0, 'NaN'::NUMERIC) g(s);$$, -false, true, false, true); - -SELECT explain_mask_costs($$ -SELECT * FROM generate_series(25.0, 2.0, 0.0) g(s);$$, -false, true, false, true); - -- -- Test SupportRequestInlineInFrom request -- @@ -443,7 +319,6 @@ SELECT pg_column_toast_chunk_id(a) IS NULL, pg_column_toast_chunk_id(b) IN (SELECT chunk_id FROM pg_toast.toastrel) FROM test_chunk_id; DROP TABLE test_chunk_id; -DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool); -- test stratnum translation support functions SELECT gist_translate_cmptype_common(7); diff --git a/postgres/regression_suite/object_address.sql b/postgres/regression_suite/object_address.sql index 48ef32df..b3f86fd3 100644 --- a/postgres/regression_suite/object_address.sql +++ b/postgres/regression_suite/object_address.sql @@ -37,6 +37,7 @@ CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig(); CREATE POLICY genpol ON addr_nsp.gentable; CREATE PROCEDURE addr_nsp.proc(int4) LANGUAGE SQL AS $$ $$; +CREATE PROPERTY GRAPH addr_nsp.gengraph; CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw; CREATE USER MAPPING FOR regress_addr_user SERVER "integer"; ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user; @@ -65,7 +66,8 @@ DECLARE objtype text; BEGIN FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), - ('toast table column'), ('view column'), ('materialized view column') + ('toast table column'), ('view column'), ('materialized view column'), + ('property graph element'), ('property graph label'), ('property graph property') LOOP BEGIN PERFORM pg_get_object_address(objtype, '{one}', '{}'); @@ -90,7 +92,7 @@ DECLARE BEGIN FOR objtype IN VALUES ('table'), ('index'), ('sequence'), ('view'), - ('materialized view'), ('foreign table'), + ('materialized view'), ('foreign table'), ('property graph'), ('table column'), ('foreign table column'), ('aggregate'), ('function'), ('procedure'), ('type'), ('cast'), ('table constraint'), ('domain constraint'), ('conversion'), ('default value'), @@ -163,6 +165,7 @@ WITH objects (type, name, args) AS (VALUES ('view', '{addr_nsp, genview}', '{}'), ('materialized view', '{addr_nsp, genmatview}', '{}'), ('foreign table', '{addr_nsp, genftable}', '{}'), + ('property graph', '{addr_nsp, gengraph}', '{}'), ('table column', '{addr_nsp, gentable, b}', '{}'), ('foreign table column', '{addr_nsp, genftable, a}', '{}'), ('aggregate', '{addr_nsp, genaggr}', '{int4}'), @@ -278,6 +281,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES ('pg_event_trigger'::regclass, 0, 0), -- no event trigger ('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL ('pg_policy'::regclass, 0, 0), -- no policy + ('pg_propgraph_element'::regclass, 0, 0), -- no property graph element + ('pg_propgraph_label'::regclass, 0, 0), -- no property graph label + ('pg_propgraph_property'::regclass, 0, 0), -- no property graph property ('pg_publication'::regclass, 0, 0), -- no publication ('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace ('pg_publication_rel'::regclass, 0, 0), -- no publication relation diff --git a/postgres/regression_suite/planner_est.sql b/postgres/regression_suite/planner_est.sql new file mode 100644 index 00000000..53210d5b --- /dev/null +++ b/postgres/regression_suite/planner_est.sql @@ -0,0 +1,150 @@ +-- +-- Tests for testing query planner selectivity and width estimates +-- +-- Most selectivity and width estimations rely too heavily on statistics +-- gathered by ANALYZE, or could vary depending on hardware. However, there +-- are a few cases where we can have more certainty about the expected number +-- of rows, or width of rows. This is a good home for such tests. +-- + +-- Function to assist with verifying EXPLAIN which includes costs. A series +-- of bool flags allows control over which portions are masked out +CREATE FUNCTION explain_mask_costs(query text, do_analyze bool, + hide_costs bool, hide_row_est bool, hide_width bool) RETURNS setof text +LANGUAGE plpgsql AS +$$ +DECLARE + ln text; + analyze_str text; +BEGIN + IF do_analyze = true THEN + analyze_str := 'on'; + ELSE + analyze_str := 'off'; + END IF; + + -- avoid jit related output by disabling it + SET LOCAL jit = 0; + + FOR ln IN + EXECUTE format('explain (analyze %s, costs on, summary off, timing off, buffers off) %s', + analyze_str, query) + LOOP + IF hide_costs = true THEN + ln := regexp_replace(ln, 'cost=\d+\.\d\d\.\.\d+\.\d\d', 'cost=N..N'); + END IF; + + IF hide_row_est = true THEN + -- don't use 'g' so that we leave the actual rows intact + ln := regexp_replace(ln, 'rows=\d+', 'rows=N'); + END IF; + + IF hide_width = true THEN + ln := regexp_replace(ln, 'width=\d+', 'width=N'); + END IF; + + RETURN NEXT ln; + END LOOP; +END; +$$; + +-- +-- Test the SupportRequestRows support function for generate_series_timestamp() +-- + +-- Ensure the row estimate matches the actual rows +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day') g(s);$$, +true, true, false, true); + +-- As above but with generate_series_timestamp +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMP '2024-02-01', TIMESTAMP '2024-03-01', INTERVAL '1 day') g(s);$$, +true, true, false, true); + +-- As above but with generate_series_timestamptz_at_zone() +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '1 day', 'UTC') g(s);$$, +true, true, false, true); + +-- Ensure the estimated and actual row counts match when the range isn't +-- evenly divisible by the step +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '7 day') g(s);$$, +true, true, false, true); + +-- Ensure the estimates match when step is decreasing +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '-1 day') g(s);$$, +true, true, false, true); + +-- Ensure an empty range estimates 1 row +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '2024-03-01', TIMESTAMPTZ '2024-02-01', INTERVAL '1 day') g(s);$$, +true, true, false, true); + +-- Ensure we get the default row estimate for infinity values +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(TIMESTAMPTZ '-infinity', TIMESTAMPTZ 'infinity', INTERVAL '1 day') g(s);$$, +false, true, false, true); + +-- Ensure the row estimate behaves correctly when step size is zero. +-- We expect generate_series_timestamp() to throw the error rather than in +-- the support function. +SELECT * FROM generate_series(TIMESTAMPTZ '2024-02-01', TIMESTAMPTZ '2024-03-01', INTERVAL '0 day') g(s); + +-- +-- Test the SupportRequestRows support function for generate_series_numeric() +-- + +-- Ensure the row estimate matches the actual rows +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(1.0, 25.0) g(s);$$, +true, true, false, true); + +-- As above but with non-default step +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(1.0, 25.0, 2.0) g(s);$$, +true, true, false, true); + +-- Ensure the estimates match when step is decreasing +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(25.0, 1.0, -1.0) g(s);$$, +true, true, false, true); + +-- Ensure an empty range estimates 1 row +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(25.0, 1.0, 1.0) g(s);$$, +true, true, false, true); + +-- Ensure we get the default row estimate for error cases (infinity/NaN values +-- and zero step size) +SELECT explain_mask_costs($$ +SELECT * FROM generate_series('-infinity'::NUMERIC, 'infinity'::NUMERIC, 1.0) g(s);$$, +false, true, false, true); + +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(1.0, 25.0, 'NaN'::NUMERIC) g(s);$$, +false, true, false, true); + +SELECT explain_mask_costs($$ +SELECT * FROM generate_series(25.0, 2.0, 0.0) g(s);$$, +false, true, false, true); + +-- +-- Test ScalarArrayOpExpr row estimates for <> ALL for arrays with NULLs. We +-- expect the planner to estimate 1 row will match in both of the following +-- tests. +-- + +-- Try a const array containing a NULL +SELECT explain_mask_costs($$ +SELECT * FROM tenk1 WHERE unique1 <> ALL (ARRAY[1, 2, 99, NULL]);$$, +false, true, false, true); + +-- Try a non-const array containing a NULL +SELECT explain_mask_costs($$ +SELECT * FROM tenk1 WHERE unique1 <> ALL (ARRAY[1, 2, 98, (SELECT 99), NULL]);$$, +false, true, false, true); + +DROP FUNCTION explain_mask_costs(text, bool, bool, bool, bool); diff --git a/postgres/regression_suite/privileges.sql b/postgres/regression_suite/privileges.sql index 65c124e5..b3ed9cfe 100644 --- a/postgres/regression_suite/privileges.sql +++ b/postgres/regression_suite/privileges.sql @@ -1838,6 +1838,60 @@ revoke select on dep_priv_test from regress_priv_user4 cascade; set session role regress_priv_user1; drop table dep_priv_test; +-- +-- Property graphs +-- +set session role regress_priv_user1; +create property graph ptg1 + vertex tables ( + atest5 key (four) + default label properties (four) + label lttc properties (three as lttck), + atest1 key (a) + default label + label lttc properties (a as lttck), + atest2 key (col1) + default label + label ltv properties (col1 as ltvk)); +-- select privileges on property graph as well as table +select * from graph_table (ptg1 match (is atest5) COLUMNS (1 as value)) limit 0; -- ok +grant select on ptg1 to regress_priv_user2; +set session role regress_priv_user2; +select * from graph_table (ptg1 match (is atest1) COLUMNS (1 as value)) limit 0; -- ok +-- select privileges on property graph but not table +select * from graph_table (ptg1 match (is atest5) COLUMNS (1 as value)) limit 0; -- fails +select * from graph_table (ptg1 match (is lttc) COLUMNS (1 as value)) limit 0; -- fails +set session role regress_priv_user3; +-- select privileges on table but not property graph +select * from graph_table (ptg1 match (is atest1) COLUMNS (1 as value)) limit 0; -- fails +-- select privileges on neither +select * from graph_table (ptg1 match (is atest5) COLUMNS (1 as value)) limit 0; -- fails +-- column privileges +set session role regress_priv_user1; +select * from graph_table (ptg1 match (v is lttc) COLUMNS (v.lttck)) limit 0; -- ok +grant select on ptg1 to regress_priv_user4; +set session role regress_priv_user4; +select * from graph_table (ptg1 match (a is atest5) COLUMNS (a.four)) limit 0; -- ok +select * from graph_table (ptg1 match (v is lttc) COLUMNS (v.lttck)) limit 0; -- fail +-- access property graph through security definer view +set session role regress_priv_user4; +create view atpgv1 as select * from graph_table (ptg1 match (is atest1) COLUMNS (1 as value)) limit 0; +grant select on atpgv1 to regress_priv_user3; +select * from atpgv1; -- ok +set session role regress_priv_user3; +select * from atpgv1; -- ok +set session role regress_priv_user4; +create view atpgv2 as select * from graph_table (ptg1 match (v is ltv) COLUMNS (v.ltvk)) limit 0; +-- though the session user is the owner of the view and also has access to the +-- property graph, it does not have access to a table referenced in the graph +-- pattern +select * from atpgv2; -- fail +grant select on atpgv2 to regress_priv_user2; +-- The user who otherwise does not have access to the property graph, gets +-- access to it through a security definer view and uses it successfully since +-- it has access to the tables referenced in the graph pattern. +set session role regress_priv_user2; +select * from atpgv2; -- ok -- clean up @@ -1845,6 +1899,10 @@ drop table dep_priv_test; drop sequence x_seq; +drop view atpgv1; +drop view atpgv2; +drop property graph ptg1; + DROP AGGREGATE priv_testagg1(int); DROP FUNCTION priv_testfunc2(int); DROP FUNCTION priv_testfunc4(boolean); @@ -2153,3 +2211,37 @@ SELECT * FROM information_schema.table_privileges t DROP TABLE grantor_test1, grantor_test2, grantor_test3; DROP ROLE regress_grantor1, regress_grantor2, regress_grantor3; + +-- GRANTED BY +CREATE ROLE regress_grantor1; +CREATE ROLE regress_grantor2 ROLE regress_grantor1; +CREATE ROLE regress_grantor3 ROLE regress_grantor1; +CREATE ROLE regress_grantor4 ROLE regress_grantor1; +CREATE ROLE regress_grantor5; +CREATE TABLE grantor_test (); +GRANT SELECT ON grantor_test TO regress_grantor2 WITH GRANT OPTION; +GRANT UPDATE ON grantor_test TO regress_grantor3 WITH GRANT OPTION; +GRANT SELECT, UPDATE ON grantor_test TO regress_grantor4 WITH GRANT OPTION; +SET ROLE regress_grantor1; + +GRANT SELECT, UPDATE ON grantor_test TO regress_grantor5; + +SELECT * FROM information_schema.table_privileges t + WHERE grantor LIKE 'regress_grantor%' ORDER BY ROW(t.*); + +REVOKE SELECT, UPDATE ON grantor_test FROM regress_grantor5; +GRANT SELECT, UPDATE ON grantor_test TO regress_grantor5 GRANTED BY regress_grantor2; +GRANT SELECT, UPDATE ON grantor_test TO regress_grantor5 GRANTED BY regress_grantor3; + +SELECT * FROM information_schema.table_privileges t + WHERE grantor LIKE 'regress_grantor%' ORDER BY ROW(t.*); + +REVOKE SELECT, UPDATE ON grantor_test FROM regress_grantor5 GRANTED BY regress_grantor2; +REVOKE SELECT, UPDATE ON grantor_test FROM regress_grantor5 GRANTED BY regress_grantor3; + +SELECT * FROM information_schema.table_privileges t + WHERE grantor LIKE 'regress_grantor%' ORDER BY ROW(t.*); + +RESET ROLE; +DROP TABLE grantor_test; +DROP ROLE regress_grantor1, regress_grantor2, regress_grantor3, regress_grantor4, regress_grantor5; diff --git a/postgres/regression_suite/sqljson.sql b/postgres/regression_suite/sqljson.sql index 436e33df..894a2301 100644 --- a/postgres/regression_suite/sqljson.sql +++ b/postgres/regression_suite/sqljson.sql @@ -386,6 +386,95 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING DROP VIEW json_array_subquery_view; +-- Test mutability of JSON_OBJECTAGG, JSON_ARRAYAGG, JSON_ARRAY, JSON_OBJECT +create type comp1 as (a int, b date); +create domain d_comp1 as comp1; +create domain mydomain as timestamptz; +create type mydomainrange as range(subtype=mydomain); +create type comp3 as (a int, b mydomainrange); +create table test_mutability( + a text[], b timestamp, c timestamptz, + d date, f1 comp1[], f2 timestamp[], + f3 d_comp1[], + f4 mydomainrange[], + f5 comp3, + f6 mydomainmultirange); + +-- JSON_OBJECTAGG, JSON_ARRAYAGG are aggregate functions, cannot be used in index +create index xx on test_mutability(json_objectagg(a: b absent on null with unique keys returning jsonb)); +create index xx on test_mutability(json_objectagg(a: b absent on null with unique keys returning json)); +create index xx on test_mutability(json_arrayagg(a returning jsonb)); +create index xx on test_mutability(json_arrayagg(a returning json)); + +-- jsonb: create expression index via json_array +create index on test_mutability(json_array(a returning jsonb)); -- ok +create index on test_mutability(json_array(b returning jsonb)); -- error +create index on test_mutability(json_array(c returning jsonb)); -- error +create index on test_mutability(json_array(d returning jsonb)); -- error +create index on test_mutability(json_array(f1 returning jsonb)); -- error +create index on test_mutability(json_array(f2 returning jsonb)); -- error +create index on test_mutability(json_array(f3 returning jsonb)); -- error +create index on test_mutability(json_array(f4 returning jsonb)); -- error +create index on test_mutability(json_array(f5 returning jsonb)); -- error +create index on test_mutability(json_array(f6 returning jsonb)); -- error + +-- jsonb: create expression index via json_object +create index on test_mutability(json_object('hello' value a returning jsonb)); -- ok +create index on test_mutability(json_object('hello' value b returning jsonb)); -- error +create index on test_mutability(json_object('hello' value c returning jsonb)); -- error +create index on test_mutability(json_object('hello' value d returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f1 returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f2 returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f3 returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f4 returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f5 returning jsonb)); -- error +create index on test_mutability(json_object('hello' value f6 returning jsonb)); -- error + +-- data type json doesn't have a default operator class for access method "btree" so +-- we use a generated column to test whether the JSON_ARRAY expression is +-- immutable +alter table test_mutability add column f10 json generated always as (json_array(a returning json)); -- ok +alter table test_mutability add column f11 json generated always as (json_array(b returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(c returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(d returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f1 returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f2 returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f3 returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f4 returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f5 returning json)); -- error +alter table test_mutability add column f11 json generated always as (json_array(f6 returning json)); -- error + +-- data type json doesn't have a default operator class for access method "btree" so +-- we use a generated column to test whether the JSON_OBJECT expression is +-- immutable +alter table test_mutability add column f11 json generated always as (json_object('hello' value a returning json)); -- ok +alter table test_mutability add column f12 json generated always as (json_object('hello' value b returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value c returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value d returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f1 returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f2 returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f3 returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f4 returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f5 returning json)); -- error +alter table test_mutability add column f12 json generated always as (json_object('hello' value f6 returning json)); -- error + +drop table test_mutability; +drop domain d_comp1; +drop type comp3; +drop type mydomainrange; +drop domain mydomain; +drop type comp1; + +-- Range/multirange with immutable subtype should be considered immutable +create type range_int as range(subtype=int); +create table test_range_immutable(r range_int, m multirange_int); +create index on test_range_immutable(json_array(r returning jsonb)); -- ok +create index on test_range_immutable(json_array(m returning jsonb)); -- ok +create index on test_range_immutable(json_object('key' value r returning jsonb)); -- ok +create index on test_range_immutable(json_object('key' value m returning jsonb)); -- ok +drop table test_range_immutable; +drop type range_int; + -- IS JSON predicate SELECT NULL IS JSON; SELECT NULL IS NOT JSON; @@ -395,6 +484,37 @@ SELECT NULL::text IS JSON; SELECT NULL::bytea IS JSON; SELECT NULL::int IS JSON; +-- IS JSON with domain types +CREATE DOMAIN jd1 AS json CHECK ((VALUE ->'a')::text <> '3'); +CREATE DOMAIN jd2 AS jsonb CHECK ((VALUE ->'a') = '1'::jsonb); +CREATE DOMAIN jd3 AS text CHECK (VALUE <> 'a'); +CREATE DOMAIN jd4 AS bytea CHECK (VALUE <> '\x61'); +CREATE DOMAIN jd5 AS date CHECK (VALUE <> NULL); + +-- NULLs through domains should return NULL (not error) +SELECT NULL::jd1 IS JSON, NULL::jd2 IS JSON, NULL::jd3 IS JSON, NULL::jd4 IS JSON; +SELECT NULL::jd1 IS NOT JSON; + +-- domain over unsupported base type should error +SELECT NULL::jd5 IS JSON; -- error +SELECT NULL::jd5 IS JSON WITH UNIQUE KEYS; -- error + +-- domain constraint violation during cast +SELECT a::jd2 IS JSON WITH UNIQUE KEYS as col1 FROM (VALUES('{"a": 1, "a": 2}')) s(a); -- error + +-- view creation and deparsing with domain IS JSON +CREATE VIEW domain_isjson AS +WITH cte(a) AS (VALUES('{"a": 1, "a": 2}')) +SELECT a::jd1 IS JSON WITH UNIQUE KEYS as jd1, + a::jd3 IS JSON WITH UNIQUE KEYS as jd3, + a::jd4 IS JSON WITH UNIQUE KEYS as jd4 +FROM cte; +-- \sv domain_isjson +SELECT * FROM domain_isjson; + +DROP VIEW domain_isjson; +DROP DOMAIN jd5, jd4, jd3, jd2, jd1; + SELECT '' IS JSON; SELECT bytea '\x00' IS JSON; diff --git a/postgres/regression_suite/stats.sql b/postgres/regression_suite/stats.sql index 3a6395ab..7dbd01c1 100644 --- a/postgres/regression_suite/stats.sql +++ b/postgres/regression_suite/stats.sql @@ -523,13 +523,33 @@ SELECT stats_reset > 'wal_reset_ts'::timestamptz FROM pg_stat_wal; -- Test error case for reset_shared with unknown stats type SELECT pg_stat_reset_shared('unknown'); --- Test that reset works for pg_stat_database +-- Test that reset works for pg_stat_database and pg_stat_database_conflicts --- Since pg_stat_database stats_reset starts out as NULL, reset it once first so we have something to compare it to +-- Since pg_stat_database stats_reset starts out as NULL, reset it once first so that we +-- have a baseline for comparison. The same for pg_stat_database_conflicts as it shares +-- the same stats_reset as pg_stat_database. SELECT pg_stat_reset(); SELECT stats_reset AS db_reset_ts FROM pg_stat_database WHERE datname = (SELECT current_database()) /* \gset */; +SELECT stats_reset AS dbc_reset_ts FROM pg_stat_database_conflicts WHERE datname = (SELECT current_database()) /* \gset */; +SELECT 'db_reset_ts'::timestamptz = 'dbc_reset_ts'::timestamptz; SELECT pg_stat_reset(); SELECT stats_reset > 'db_reset_ts'::timestamptz FROM pg_stat_database WHERE datname = (SELECT current_database()); +SELECT stats_reset > 'dbc_reset_ts'::timestamptz FROM pg_stat_database_conflicts WHERE datname = (SELECT current_database()); + +-- Test that reset works for pg_statio_all_sequences + +-- Use the sequence to accumulate its stats, and reset them once first +-- so that we have a baseline for comparison, similar to the previous test. +-- stats_reset to compare to. +CREATE SEQUENCE test_seq1; +SELECT nextval('test_seq1'); +SELECT pg_stat_reset_single_table_counters('test_seq1'::regclass); +SELECT stats_reset AS seq_reset_ts + FROM pg_statio_all_sequences WHERE relname ='test_seq1' /* \gset */; +SELECT pg_stat_reset_single_table_counters('test_seq1'::regclass); +SELECT stats_reset > 'seq_reset_ts'::timestamptz + FROM pg_statio_all_sequences WHERE relname ='test_seq1'; +DROP SEQUENCE test_seq1; ---- diff --git a/postgres/regression_suite/stats_import.sql b/postgres/regression_suite/stats_import.sql index 3db1e329..0b5fb897 100644 --- a/postgres/regression_suite/stats_import.sql +++ b/postgres/regression_suite/stats_import.sql @@ -1,5 +1,170 @@ CREATE SCHEMA stats_import; +-- +-- Convenience view for columns of pg_stats that are stable across test runs. +-- +CREATE VIEW stats_import.pg_stats_stable AS + SELECT schemaname, tablename, attname, inherited, null_frac, avg_width, + n_distinct, most_common_vals::text as most_common_vals, + most_common_freqs, histogram_bounds::text AS histogram_bounds, + correlation, most_common_elems::text AS most_common_elems, + most_common_elem_freqs, elem_count_histogram, + range_length_histogram::text AS range_length_histogram, range_empty_frac, + range_bounds_histogram::text AS range_bounds_histogram + FROM pg_stats; + +-- +-- Setup functions for set-difference convenience functions +-- + +-- Test to detect any new columns added to pg_statistic. If any columns +-- are added, we may need to update pg_statistic_flat() and the facilities +-- we are testing. +SELECT COUNT(*) FROM pg_attribute + WHERE attrelid = 'pg_catalog.pg_statistic'::regclass AND + attnum > 0; + +-- Create a view that is used purely for the type based on pg_statistic. +CREATE VIEW stats_import.pg_statistic_flat_t AS + SELECT + a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, + s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, + s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, + s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, + s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, + s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, + s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, + s.stavalues5::text AS sv5 + FROM pg_statistic s + JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum + WHERE FALSE; + +-- Function to retrieve data used for diff comparisons between two +-- relations based on the contents of pg_statistic. +CREATE FUNCTION stats_import.pg_statistic_flat(p_relname text) +RETURNS SETOF stats_import.pg_statistic_flat_t +BEGIN ATOMIC + SELECT a.attname, s.stainherit, s.stanullfrac, s.stawidth, + s.stadistinct, s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, + s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, s.stacoll1, s.stacoll2, + s.stacoll3, s.stacoll4, s.stacoll5, s.stanumbers1, s.stanumbers2, + s.stanumbers3, s.stanumbers4, s.stanumbers5, s.stavalues1::text, + s.stavalues2::text, s.stavalues3::text, + s.stavalues4::text, s.stavalues5::text + FROM pg_statistic s + JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum + JOIN pg_class c ON c.oid = a.attrelid + WHERE c.relnamespace = 'stats_import'::regnamespace + AND c.relname = p_relname; +END; + +-- Comparison function for pg_statistic. The two relations defined by +-- the function caller are compared. +CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text) +RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t) +BEGIN ATOMIC + WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)), + bset AS (SELECT * FROM stats_import.pg_statistic_flat(b)) + SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t + FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b + UNION ALL + SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t + FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; +END; + +-- Test to detect any new columns added to pg_stats_ext. If any columns +-- are added, we may need to update pg_stats_ext_flat() and the facilities +-- we are testing. +SELECT COUNT(*) FROM pg_attribute + WHERE attrelid = 'pg_catalog.pg_stats_ext'::regclass AND + attnum > 0; + +-- Create a view that is used purely for the type based on pg_stats_ext. +CREATE VIEW stats_import.pg_stats_ext_flat_t AS + SELECT inherited, n_distinct, dependencies, most_common_vals, + most_common_freqs, most_common_base_freqs + FROM pg_stats_ext + WHERE FALSE; + +-- Function to retrieve data used for diff comparisons between two +-- relations based on the contents of pg_stats_ext. +CREATE FUNCTION stats_import.pg_stats_ext_flat(p_statname text) +RETURNS SETOF stats_import.pg_stats_ext_flat_t +BEGIN ATOMIC + SELECT inherited, n_distinct, dependencies, most_common_vals, + most_common_freqs, most_common_base_freqs + FROM pg_stats_ext + WHERE statistics_schemaname = 'stats_import' + AND statistics_name = p_statname; +END; + +-- Comparison function for pg_stats_ext. The two relations defined by +-- the function caller are compared. +CREATE FUNCTION stats_import.pg_stats_ext_get_difference(a text, b text) +RETURNS TABLE (statname text, stats stats_import.pg_stats_ext_flat_t) +BEGIN ATOMIC + WITH aset AS (SELECT * FROM stats_import.pg_stats_ext_flat(a)), + bset AS (SELECT * FROM stats_import.pg_stats_ext_flat(b)) + SELECT a AS relname, a_minus_b::stats_import.pg_stats_ext_flat_t + FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b + UNION ALL + SELECT b AS relname, b_minus_a::stats_import.pg_stats_ext_flat_t + FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; +END; + +-- Test to detect any new columns added to pg_stats_ext_exprs. If any columns +-- are added, we may need to update pg_stats_ext_exprs_flat() and the facilities +-- we are testing. +SELECT COUNT(*) FROM pg_attribute + WHERE attrelid = 'pg_catalog.pg_stats_ext_exprs'::regclass AND + attnum > 0; + +-- Create a view that is used purely for the type based on pg_stats_ext_exprs. +CREATE VIEW stats_import.pg_stats_ext_exprs_flat_t AS + SELECT inherited, null_frac, avg_width, n_distinct, + most_common_vals::text AS most_common_vals, + most_common_freqs, histogram_bounds::text AS histogram_bounds, + correlation, most_common_elems::text AS most_common_elems, + most_common_elem_freqs, elem_count_histogram, + range_length_histogram::text AS range_length_histogram, + range_empty_frac, range_bounds_histogram::text AS range_bounds_histogram + FROM pg_stats_ext_exprs AS n + WHERE FALSE; + +-- Function to retrieve data used for diff comparisons between two +-- relations based on the contents of pg_stats_ext_exprs. +CREATE FUNCTION stats_import.pg_stats_ext_exprs_flat(p_statname text) +RETURNS SETOF stats_import.pg_stats_ext_exprs_flat_t +BEGIN ATOMIC + SELECT inherited, null_frac, avg_width, n_distinct, + most_common_vals::text AS most_common_vals, + most_common_freqs, histogram_bounds::text AS histogram_bounds, + correlation, most_common_elems::text AS most_common_elems, + most_common_elem_freqs, elem_count_histogram, + range_length_histogram::text AS range_length_histogram, + range_empty_frac, range_bounds_histogram::text AS range_bounds_histogram + FROM pg_stats_ext_exprs AS n + WHERE n.statistics_schemaname = 'stats_import' AND + n.statistics_name = p_statname; +END; + +-- Comparison function for pg_stats_ext_exprs. The two relations defined by +-- the function caller are compared. +CREATE FUNCTION stats_import.pg_stats_ext_exprs_get_difference(a text, b text) +RETURNS TABLE (statname text, stats stats_import.pg_stats_ext_exprs_flat_t) +BEGIN ATOMIC + WITH aset AS (SELECT * FROM stats_import.pg_stats_ext_exprs_flat(a)), + bset AS (SELECT * FROM stats_import.pg_stats_ext_exprs_flat(b)) + SELECT a AS relname, a_minus_b::stats_import.pg_stats_ext_exprs_flat_t + FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b + UNION ALL + SELECT b AS relname, b_minus_a::stats_import.pg_stats_ext_exprs_flat_t + FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a; +END; + +-- +-- Schema setup. +-- CREATE TYPE stats_import.complex_type AS ( a integer, b real, @@ -369,7 +534,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( 'n_distinct', 0.6::real); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -388,7 +553,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( 'null_frac', 0.4::real); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -404,7 +569,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( 'nope', 0.5::real); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -421,7 +586,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -438,7 +603,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -456,7 +621,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -474,7 +639,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -491,7 +656,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -508,7 +673,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -524,7 +689,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -541,7 +706,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -558,7 +723,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -576,7 +741,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -593,7 +758,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -610,7 +775,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -627,7 +792,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -644,7 +809,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -660,7 +825,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -678,7 +843,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -696,7 +861,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -713,7 +878,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -730,7 +895,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -747,7 +912,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -764,7 +929,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( ); SELECT * -FROM pg_stats +FROM stats_import.pg_stats_stable WHERE schemaname = 'stats_import' AND tablename = 'test' AND inherited = false @@ -884,113 +1049,13 @@ AND c.relname IN ('test', 'test_clone', 'is_odd', 'is_odd_clone') GROUP BY c.relname ORDER BY c.relname; --- check test minus test_clone -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'test' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.test'::regclass -EXCEPT -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'test' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.test_clone'::regclass; - --- check test_clone minus test -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'test_clone' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.test_clone'::regclass -EXCEPT -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'test_clone' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.test'::regclass; - --- check is_odd minus is_odd_clone -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'is_odd' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.is_odd'::regclass -EXCEPT -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'is_odd' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.is_odd_clone'::regclass; - --- check is_odd_clone minus is_odd -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'is_odd_clone' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.is_odd_clone'::regclass -EXCEPT -SELECT - a.attname, s.stainherit, s.stanullfrac, s.stawidth, s.stadistinct, - s.stakind1, s.stakind2, s.stakind3, s.stakind4, s.stakind5, - s.staop1, s.staop2, s.staop3, s.staop4, s.staop5, - s.stacoll1, s.stacoll2, s.stacoll3, s.stacoll4, s.stacoll5, - s.stanumbers1, s.stanumbers2, s.stanumbers3, s.stanumbers4, s.stanumbers5, - s.stavalues1::text AS sv1, s.stavalues2::text AS sv2, - s.stavalues3::text AS sv3, s.stavalues4::text AS sv4, - s.stavalues5::text AS sv5, 'is_odd_clone' AS direction -FROM pg_statistic s -JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum -WHERE s.starelid = 'stats_import.is_odd'::regclass; +SELECT relname, (stats).* +FROM stats_import.pg_statistic_get_difference('test', 'test_clone') +/* \gx */; + +SELECT relname, (stats).* +FROM stats_import.pg_statistic_get_difference('is_odd', 'is_odd_clone') +/* \gx */; -- attribute stats exist before a clear, but not after SELECT COUNT(*) @@ -2171,96 +2236,14 @@ CROSS JOIN LATERAL ( WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_stat'; --- Set difference old MINUS new. -SELECT o.inherited, - o.n_distinct, o.dependencies, o.most_common_vals, - o.most_common_freqs, o.most_common_base_freqs - FROM pg_stats_ext AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_stat' -EXCEPT -SELECT n.inherited, - n.n_distinct, n.dependencies, n.most_common_vals, - n.most_common_freqs, n.most_common_base_freqs - FROM pg_stats_ext AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_stat_clone'; --- Set difference new MINUS old. -SELECT n.inherited, - n.n_distinct, n.dependencies, n.most_common_vals, - n.most_common_freqs, n.most_common_base_freqs - FROM pg_stats_ext AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_stat_clone' -EXCEPT -SELECT o.inherited, - o.n_distinct, o.dependencies, o.most_common_vals, - o.most_common_freqs, o.most_common_base_freqs - FROM pg_stats_ext AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_stat'; - --- Set difference for exprs: old MINUS new. -SELECT o.inherited, - o.null_frac, o.avg_width, o.n_distinct, - o.most_common_vals::text AS most_common_vals, - o.most_common_freqs, - o.histogram_bounds::text AS histogram_bounds, - o.correlation, - o.most_common_elems::text AS most_common_elems, - o.most_common_elem_freqs, o.elem_count_histogram, - o.range_length_histogram::text AS range_length_histogram, - o.range_empty_frac, - o.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_stat' -EXCEPT -SELECT n.inherited, - n.null_frac, n.avg_width, n.n_distinct, - n.most_common_vals::text AS most_common_vals, - n.most_common_freqs, - n.histogram_bounds::text AS histogram_bounds, - n.correlation, - n.most_common_elems::text AS most_common_elems, - n.most_common_elem_freqs, n.elem_count_histogram, - n.range_length_histogram::text AS range_length_histogram, - n.range_empty_frac, - n.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_stat_clone'; - --- Set difference for exprs: new MINUS old. -SELECT n.inherited, - n.null_frac, n.avg_width, n.n_distinct, - n.most_common_vals::text AS most_common_vals, - n.most_common_freqs, - n.histogram_bounds::text AS histogram_bounds, - n.correlation, - n.most_common_elems::text AS most_common_elems, - n.most_common_elem_freqs, n.elem_count_histogram, - n.range_length_histogram::text AS range_length_histogram, - n.range_empty_frac, - n.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_stat_clone' -EXCEPT -SELECT o.inherited, - o.null_frac, o.avg_width, o.n_distinct, - o.most_common_vals::text AS most_common_vals, - o.most_common_freqs, - o.histogram_bounds::text AS histogram_bounds, - o.correlation, - o.most_common_elems::text AS most_common_elems, - o.most_common_elem_freqs, o.elem_count_histogram, - o.range_length_histogram::text AS range_length_histogram, - o.range_empty_frac, - o.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_stat'; +SELECT statname, (stats).* +FROM stats_import.pg_stats_ext_get_difference('test_stat', 'test_stat_clone') +/* \gx */; + +SELECT statname, (stats).* +FROM stats_import.pg_stats_ext_exprs_get_difference('test_stat', 'test_stat_clone') +/* \gx */; + ANALYZE stats_import.test_mr; @@ -2302,99 +2285,16 @@ CROSS JOIN LATERAL ( WHERE e.statistics_schemaname = 'stats_import' AND e.statistics_name = 'test_mr_stat'; --- Set difference old MINUS new. -SELECT o.inherited, - o.n_distinct, o.dependencies, o.most_common_vals, - o.most_common_freqs, o.most_common_base_freqs - FROM pg_stats_ext AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_mr_stat' -EXCEPT -SELECT n.inherited, - n.n_distinct, n.dependencies, n.most_common_vals, - n.most_common_freqs, n.most_common_base_freqs - FROM pg_stats_ext AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_mr_stat_clone'; --- Set difference new MINUS old. -SELECT n.inherited, - n.n_distinct, n.dependencies, n.most_common_vals, - n.most_common_freqs, n.most_common_base_freqs - FROM pg_stats_ext AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_mr_stat_clone' -EXCEPT -SELECT o.inherited, - o.n_distinct, o.dependencies, o.most_common_vals, - o.most_common_freqs, o.most_common_base_freqs - FROM pg_stats_ext AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_mr_stat'; - --- Set difference for exprs: old MINUS new. -SELECT o.inherited, - o.null_frac, o.avg_width, o.n_distinct, - o.most_common_vals::text AS most_common_vals, - o.most_common_freqs, - o.histogram_bounds::text AS histogram_bounds, - o.correlation, - o.most_common_elems::text AS most_common_elems, - o.most_common_elem_freqs, o.elem_count_histogram, - o.range_length_histogram::text AS range_length_histogram, - o.range_empty_frac, - o.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_mr_stat' -EXCEPT -SELECT n.inherited, - n.null_frac, n.avg_width, n.n_distinct, - n.most_common_vals::text AS most_common_vals, - n.most_common_freqs, - n.histogram_bounds::text AS histogram_bounds, - n.correlation, - n.most_common_elems::text AS most_common_elems, - n.most_common_elem_freqs, n.elem_count_histogram, - n.range_length_histogram::text AS range_length_histogram, - n.range_empty_frac, - n.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_mr_stat_clone'; - --- Set difference for exprs: new MINUS old. -SELECT n.inherited, - n.null_frac, n.avg_width, n.n_distinct, - n.most_common_vals::text AS most_common_vals, - n.most_common_freqs, - n.histogram_bounds::text AS histogram_bounds, - n.correlation, - n.most_common_elems::text AS most_common_elems, - n.most_common_elem_freqs, n.elem_count_histogram, - n.range_length_histogram::text AS range_length_histogram, - n.range_empty_frac, - n.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS n - WHERE n.statistics_schemaname = 'stats_import' AND - n.statistics_name = 'test_mr_stat_clone' -EXCEPT -SELECT o.inherited, - o.null_frac, o.avg_width, o.n_distinct, - o.most_common_vals::text AS most_common_vals, - o.most_common_freqs, - o.histogram_bounds::text AS histogram_bounds, - o.correlation, - o.most_common_elems::text AS most_common_elems, - o.most_common_elem_freqs, o.elem_count_histogram, - o.range_length_histogram::text AS range_length_histogram, - o.range_empty_frac, - o.range_bounds_histogram::text AS range_bounds_histogram - FROM pg_stats_ext_exprs AS o - WHERE o.statistics_schemaname = 'stats_import' AND - o.statistics_name = 'test_mr_stat'; +SELECT statname, (stats).* +FROM stats_import.pg_stats_ext_get_difference('test_mr_stat', 'test_mr_stat_clone') +/* \gx */; + +SELECT statname, (stats).* +FROM stats_import.pg_stats_ext_exprs_get_difference('test_mr_stat', 'test_mr_stat_clone') +/* \gx */; -- range_length_histogram, range_empty_frac, and range_bounds_histogram --- have been added to pg_stat_ext_exprs in PostgreSQL 19. When dumping +-- have been added to pg_stats_ext_exprs in PostgreSQL 19. When dumping -- expression statistics in a cluster with an older version, these fields -- are dumped as NULL, pg_restore_extended_stats() authorizing the partial -- restore state of the extended statistics data. This test emulates such diff --git a/postgres/regression_suite/subselect.sql b/postgres/regression_suite/subselect.sql index e2e89d31..a4b70517 100644 --- a/postgres/regression_suite/subselect.sql +++ b/postgres/regression_suite/subselect.sql @@ -1448,3 +1448,188 @@ SELECT * FROM onek t1, lateral (SELECT * FROM onek t2 WHERE t2.ten IN (values (t -- VtA causes the whole expression to be evaluated as a constant EXPLAIN (COSTS OFF) SELECT ten FROM onek t WHERE 1.0::integer IN ((VALUES (1), (3))); + +-- +-- Check NOT IN performs an ANTI JOIN when both the outer query's expressions +-- and the sub-select's output columns are provably non-nullable, and the +-- operator itself cannot return NULL for non-null inputs. +-- + +BEGIN; + +CREATE TEMP TABLE not_null_tab (id int NOT NULL, val int NOT NULL); +CREATE TEMP TABLE null_tab (id int, val int); + +-- ANTI JOIN: both sides are defined NOT NULL +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT id FROM not_null_tab); + +-- No ANTI JOIN: outer side is nullable +EXPLAIN (COSTS OFF) +SELECT * FROM null_tab +WHERE id NOT IN (SELECT id FROM not_null_tab); + +-- No ANTI JOIN: inner side is nullable +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT id FROM null_tab); + +-- ANTI JOIN: outer side is defined NOT NULL, inner side is forced nonnullable +-- by qual clause +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT id FROM null_tab WHERE id IS NOT NULL); + +-- No ANTI JOIN: outer side is nullable (we don't check outer query quals for now) +EXPLAIN (COSTS OFF) +SELECT * FROM null_tab +WHERE id IS NOT NULL + AND id NOT IN (SELECT id FROM not_null_tab); + +-- ANTI JOIN: outer side is defined NOT NULL, inner side is defined NOT NULL +-- and is not nulled by outer join +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT t1.id + FROM not_null_tab t1 + LEFT JOIN not_null_tab t2 ON t1.id = t2.id +); + +-- No ANTI JOIN: inner side is defined NOT NULL but is nulled by outer join +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT t2.id + FROM not_null_tab t1 + LEFT JOIN not_null_tab t2 ON t1.id = t2.id +); + +-- ANTI JOIN: outer side is defined NOT NULL, inner side is forced nonnullable +-- by qual clause +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT t2.id + FROM not_null_tab t1 + LEFT JOIN not_null_tab t2 ON t1.id = t2.id + WHERE t2.id IS NOT NULL +); + +-- ANTI JOIN: outer side is defined NOT NULL, inner side is forced nonnullable +-- by qual clause +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT t1.id + FROM null_tab t1 + LEFT JOIN null_tab t2 ON t1.id = t2.id + WHERE t1.id IS NOT NULL +); + +-- ANTI JOIN: outer side is defined NOT NULL, inner side is forced nonnullable +-- by qual clause +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT t1.id + FROM null_tab t1 + INNER JOIN null_tab t2 ON t1.id = t2.id + LEFT JOIN null_tab t3 ON TRUE +); + +-- ANTI JOIN: outer side is defined NOT NULL and is not nulled by outer join, +-- inner side is defined NOT NULL +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab t1 +LEFT JOIN not_null_tab t2 ON t1.id = t2.id +WHERE t1.id NOT IN (SELECT id FROM not_null_tab); + +-- No ANTI JOIN: outer side is nulled by outer join +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab t1 +LEFT JOIN not_null_tab t2 ON t1.id = t2.id +WHERE t2.id NOT IN (SELECT id FROM not_null_tab); + +-- No ANTI JOIN: sublink is in an outer join's ON qual and references the +-- non-nullable side +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab t1 +LEFT JOIN not_null_tab t2 +ON t1.id NOT IN (SELECT id FROM not_null_tab); + +-- ANTI JOIN: outer side is defined NOT NULL and is not nulled by outer join, +-- inner side is defined NOT NULL +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab t1 +LEFT JOIN not_null_tab t2 +ON t2.id NOT IN (SELECT id FROM not_null_tab); + +-- ANTI JOIN: both sides are defined NOT NULL +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE (id, val) NOT IN (SELECT id, val FROM not_null_tab); + +-- ANTI JOIN: both sides are defined NOT NULL +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE NOT (id, val) > ANY (SELECT id, val FROM not_null_tab); + +-- No ANTI JOIN: one column of the outer side is nullable +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab t1, null_tab t2 +WHERE (t1.id, t2.id) NOT IN (SELECT id, val FROM not_null_tab); + +-- No ANTI JOIN: one column of the inner side is nullable +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE (id, val) NOT IN (SELECT t1.id, t2.id FROM not_null_tab t1, null_tab t2); + +-- ANTI JOIN: COALESCE(nullable, constant) is non-nullable +EXPLAIN (COSTS OFF) +SELECT * FROM null_tab +WHERE COALESCE(id, -1) NOT IN (SELECT id FROM not_null_tab); + +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT COALESCE(id, -1) FROM null_tab); + +-- ANTI JOIN: GROUP BY (without Grouping Sets) preserves the non-nullability of +-- the column +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT id FROM not_null_tab GROUP BY id); + +-- No ANTI JOIN: GROUP BY on a nullable column +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN (SELECT id FROM null_tab GROUP BY id); + +-- No ANTI JOIN: Grouping Sets can introduce NULLs +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE id NOT IN ( + SELECT id + FROM not_null_tab + GROUP BY GROUPING SETS ((id), (val)) +); + +-- create a custom "unsafe" equality operator +CREATE FUNCTION int4eq_unsafe(int4, int4) + RETURNS bool + AS 'int4eq' + LANGUAGE internal IMMUTABLE; + +CREATE OPERATOR ?= ( + PROCEDURE = int4eq_unsafe, + LEFTARG = int4, + RIGHTARG = int4 +); + +-- No ANTI JOIN: the operator is not safe +EXPLAIN (COSTS OFF) +SELECT * FROM not_null_tab +WHERE NOT id ?= ANY (SELECT id FROM not_null_tab); + +ROLLBACK; diff --git a/postgres/regression_suite/typed_table.sql b/postgres/regression_suite/typed_table.sql index afb317a4..13f2aafa 100644 --- a/postgres/regression_suite/typed_table.sql +++ b/postgres/regression_suite/typed_table.sql @@ -20,7 +20,8 @@ ALTER TABLE persons DROP COLUMN name; ALTER TABLE persons RENAME COLUMN id TO num; ALTER TABLE persons ALTER COLUMN name TYPE varchar; CREATE TABLE stuff (id int); -ALTER TABLE persons INHERIT stuff; +ALTER TABLE persons INHERIT stuff; -- error +ALTER TABLE persons NO INHERIT stuff; -- error CREATE TABLE personsx OF person_type (myname WITH OPTIONS NOT NULL); -- error diff --git a/postgres/regression_suite/uuid.sql b/postgres/regression_suite/uuid.sql index 465153a0..f512f4de 100644 --- a/postgres/regression_suite/uuid.sql +++ b/postgres/regression_suite/uuid.sql @@ -146,6 +146,11 @@ SELECT uuid_extract_timestamp('017F22E2-79B0-7CC3-98C4-DC0C0C07398F') = 'Tuesday SELECT uuid_extract_timestamp(gen_random_uuid()); -- null SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111'); -- null +-- casts +SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea; +SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid; +SELECT '\x1234567890abcdef'::bytea::uuid; -- error +SELECT v = v::bytea::uuid as matched FROM gen_random_uuid() v; -- clean up DROP TABLE guid1, guid2, guid3 CASCADE;