diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d90a89 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + name: cargo test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y clang libclang-dev + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Run tests + run: cargo test diff --git a/build.rs b/build.rs index 1fc3cd6..bda787d 100644 --- a/build.rs +++ b/build.rs @@ -1312,6 +1312,17 @@ fn build_node_struct(s: &syn::ItemStruct, type_comment_regex: &Regex) -> NodeStr // Comment claims args is A_Const, but that isn't the case for // `SET TRANSACTION ...` (("VariableSetStmt", "args"), NodeFieldType::List), + // Raw range partition bounds can contain A_Const for concrete values + // and ColumnRef for MINVALUE/MAXVALUE, not only PartitionRangeDatum. + (("PartitionBoundSpec", "lowerdatums"), NodeFieldType::List), + (("PartitionBoundSpec", "upperdatums"), NodeFieldType::List), + // The raw parser stores text-search token types as Integer nodes, not + // String nodes, and dicts is a nested list of qualified names. + ( + ("AlterTSConfigurationStmt", "tokentype"), + NodeFieldType::List, + ), + (("AlterTSConfigurationStmt", "dicts"), NodeFieldType::List), ]; let attrs = clean_doc_comments(&s.attrs); diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 0000000..a343576 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] + +use pg_raw_parse::{parse, walk::walk}; + +pub fn run_parse_debug_case(query: &str) { + let ast = parse(query).unwrap_or_else(|err| panic!("failed to parse `{query}`: {err:?}")); + assert!(!format!("{:?}", ast).is_empty()) +} + +pub fn run_parse_debug_cases(cases: &[&str]) { + for query in cases { + run_parse_debug_case(query); + } +} + +pub fn run_parse_walk_case(query: &str) { + let ast = parse(query).unwrap_or_else(|err| panic!("failed to parse `{query}`: {err:?}")); + walk(ast.stmts().next().unwrap(), |node| { + assert!(!format!("{:?}", node).is_empty()); + }); + assert!(!format!("{:?}", ast).is_empty()) +} + +pub fn run_parse_walk_cases(cases: &[&str]) { + for query in cases { + run_parse_walk_case(query); + } +} diff --git a/tests/test_alter_aggregate.rs b/tests/test_alter_aggregate.rs new file mode 100644 index 0000000..266d22a --- /dev/null +++ b/tests/test_alter_aggregate.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_aggregate_parses() { + run_cases(&[ + r#"ALTER AGGREGATE my_sum(integer) RENAME TO my_sum_int"#, + r#"ALTER AGGREGATE my_sum(integer) OWNER TO CURRENT_USER"#, + r#"ALTER AGGREGATE my_sum(integer) SET SCHEMA analytics"#, + ]); +} diff --git a/tests/test_alter_collation.rs b/tests/test_alter_collation.rs new file mode 100644 index 0000000..3ea2949 --- /dev/null +++ b/tests/test_alter_collation.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_collation_parses() { + run_cases(&[ + r#"ALTER COLLATION my_collation REFRESH VERSION"#, + r#"ALTER COLLATION my_collation RENAME TO my_new_collation"#, + r#"ALTER COLLATION my_collation OWNER TO CURRENT_USER"#, + r#"ALTER COLLATION my_collation SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_conversion.rs b/tests/test_alter_conversion.rs new file mode 100644 index 0000000..e2bff03 --- /dev/null +++ b/tests/test_alter_conversion.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_conversion_parses() { + run_cases(&[ + r#"ALTER CONVERSION my_conversion RENAME TO my_new_conversion"#, + r#"ALTER CONVERSION my_conversion OWNER TO CURRENT_USER"#, + r#"ALTER CONVERSION my_conversion SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_database.rs b/tests/test_alter_database.rs new file mode 100644 index 0000000..2968e41 --- /dev/null +++ b/tests/test_alter_database.rs @@ -0,0 +1,18 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_database_parses() { + run_cases(&[ + r#"ALTER DATABASE mydb WITH ALLOW_CONNECTIONS false"#, + r#"ALTER DATABASE mydb RENAME TO mydb2"#, + r#"ALTER DATABASE mydb OWNER TO CURRENT_USER"#, + r#"ALTER DATABASE mydb SET TABLESPACE fastspace"#, + r#"ALTER DATABASE mydb SET search_path TO public, extensions"#, + r#"ALTER DATABASE mydb SET work_mem = '64MB'"#, + r#"ALTER DATABASE mydb SET enable_seqscan FROM CURRENT"#, + r#"ALTER DATABASE mydb RESET work_mem"#, + r#"ALTER DATABASE mydb RESET ALL"#, + ]); +} diff --git a/tests/test_alter_default_privileges.rs b/tests/test_alter_default_privileges.rs new file mode 100644 index 0000000..d11df23 --- /dev/null +++ b/tests/test_alter_default_privileges.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_default_privileges_parses() { + run_cases(&[ + r#"ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app_user"#, + r#"ALTER DEFAULT PRIVILEGES FOR ROLE owner_role IN SCHEMA public GRANT USAGE ON TYPES TO app_user WITH GRANT OPTION"#, + r#"ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE GRANT OPTION FOR SELECT ON TABLES FROM app_user CASCADE"#, + r#"ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON ROUTINES FROM PUBLIC RESTRICT"#, + ]); +} diff --git a/tests/test_alter_domain.rs b/tests/test_alter_domain.rs new file mode 100644 index 0000000..d5f7881 --- /dev/null +++ b/tests/test_alter_domain.rs @@ -0,0 +1,20 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_domain_parses() { + run_cases(&[ + r#"ALTER DOMAIN email_domain SET DEFAULT 'unknown@example.com'"#, + r#"ALTER DOMAIN email_domain DROP DEFAULT"#, + r#"ALTER DOMAIN email_domain SET NOT NULL"#, + r#"ALTER DOMAIN email_domain DROP NOT NULL"#, + r#"ALTER DOMAIN email_domain ADD CONSTRAINT email_check CHECK (VALUE LIKE '%@%') NOT VALID"#, + r#"ALTER DOMAIN email_domain DROP CONSTRAINT IF EXISTS email_check CASCADE"#, + r#"ALTER DOMAIN email_domain RENAME CONSTRAINT email_check TO email_check_new"#, + r#"ALTER DOMAIN email_domain VALIDATE CONSTRAINT email_check"#, + r#"ALTER DOMAIN email_domain OWNER TO CURRENT_USER"#, + r#"ALTER DOMAIN email_domain RENAME TO email_domain_new"#, + r#"ALTER DOMAIN email_domain SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_event_trigger.rs b/tests/test_alter_event_trigger.rs new file mode 100644 index 0000000..e91bd53 --- /dev/null +++ b/tests/test_alter_event_trigger.rs @@ -0,0 +1,15 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_event_trigger_parses() { + run_cases(&[ + r#"ALTER EVENT TRIGGER trg DISABLE"#, + r#"ALTER EVENT TRIGGER trg ENABLE"#, + r#"ALTER EVENT TRIGGER trg ENABLE REPLICA"#, + r#"ALTER EVENT TRIGGER trg ENABLE ALWAYS"#, + r#"ALTER EVENT TRIGGER trg OWNER TO CURRENT_USER"#, + r#"ALTER EVENT TRIGGER trg RENAME TO trg_new"#, + ]); +} diff --git a/tests/test_alter_extension.rs b/tests/test_alter_extension.rs new file mode 100644 index 0000000..ddd871b --- /dev/null +++ b/tests/test_alter_extension.rs @@ -0,0 +1,14 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_extension_parses() { + run_cases(&[ + r#"ALTER EXTENSION hstore UPDATE"#, + r#"ALTER EXTENSION hstore UPDATE TO '1.8'"#, + r#"ALTER EXTENSION hstore SET SCHEMA public"#, + r#"ALTER EXTENSION hstore ADD TABLE my_table"#, + r#"ALTER EXTENSION hstore DROP FUNCTION my_function(integer)"#, + ]); +} diff --git a/tests/test_alter_foreign_data_wrapper.rs b/tests/test_alter_foreign_data_wrapper.rs new file mode 100644 index 0000000..005d2be --- /dev/null +++ b/tests/test_alter_foreign_data_wrapper.rs @@ -0,0 +1,16 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_foreign_data_wrapper_parses() { + run_cases(&[ + r#"ALTER FOREIGN DATA WRAPPER my_fdw HANDLER my_handler"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw NO HANDLER"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw VALIDATOR my_validator"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw NO VALIDATOR"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw OPTIONS (ADD host 'localhost', SET port '5432', DROP oldopt)"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw OWNER TO CURRENT_USER"#, + r#"ALTER FOREIGN DATA WRAPPER my_fdw RENAME TO my_fdw_new"#, + ]); +} diff --git a/tests/test_alter_foreign_table.rs b/tests/test_alter_foreign_table.rs new file mode 100644 index 0000000..9a88aa5 --- /dev/null +++ b/tests/test_alter_foreign_table.rs @@ -0,0 +1,28 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_foreign_table_parses() { + run_cases(&[ + r#"ALTER FOREIGN TABLE IF EXISTS ONLY ft ADD COLUMN IF NOT EXISTS payload text OPTIONS (column_name 'payload')"#, + r#"ALTER FOREIGN TABLE ft DROP COLUMN IF EXISTS old_col CASCADE"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload TYPE varchar(200)"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload SET DEFAULT 'x'"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload DROP DEFAULT"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload SET NOT NULL"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload DROP NOT NULL"#, + r#"ALTER FOREIGN TABLE ft ALTER COLUMN payload SET STATISTICS 100"#, + r#"ALTER FOREIGN TABLE ft ADD CHECK (payload <> '') NOT VALID"#, + r#"ALTER FOREIGN TABLE ft VALIDATE CONSTRAINT payload_check"#, + r#"ALTER FOREIGN TABLE ft DROP CONSTRAINT IF EXISTS payload_check RESTRICT"#, + r#"ALTER FOREIGN TABLE ft DISABLE TRIGGER ALL"#, + r#"ALTER FOREIGN TABLE ft ENABLE ALWAYS TRIGGER trg"#, + r#"ALTER FOREIGN TABLE ft SET WITHOUT OIDS"#, + r#"ALTER FOREIGN TABLE ft OWNER TO CURRENT_USER"#, + r#"ALTER FOREIGN TABLE ft OPTIONS (ADD schema_name 'public', SET table_name 'remote_table')"#, + r#"ALTER FOREIGN TABLE ft RENAME COLUMN payload TO body"#, + r#"ALTER FOREIGN TABLE ft RENAME TO ft_new"#, + r#"ALTER FOREIGN TABLE ft SET SCHEMA archive"#, + ]); +} diff --git a/tests/test_alter_function.rs b/tests/test_alter_function.rs new file mode 100644 index 0000000..a2173b9 --- /dev/null +++ b/tests/test_alter_function.rs @@ -0,0 +1,19 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_function_parses() { + run_cases(&[ + r#"ALTER FUNCTION my_function(integer) CALLED ON NULL INPUT"#, + r#"ALTER FUNCTION my_function(integer) RETURNS NULL ON NULL INPUT"#, + r#"ALTER FUNCTION my_function(integer) STRICT"#, + r#"ALTER FUNCTION my_function(integer) IMMUTABLE PARALLEL SAFE COST 5 ROWS 10 SET search_path TO public"#, + r#"ALTER FUNCTION my_function(integer) RESET ALL"#, + r#"ALTER FUNCTION my_function(integer) OWNER TO CURRENT_USER"#, + r#"ALTER FUNCTION my_function(integer) RENAME TO my_function_new"#, + r#"ALTER FUNCTION my_function(integer) SET SCHEMA public"#, + r#"ALTER FUNCTION my_function(integer) DEPENDS ON EXTENSION hstore"#, + r#"ALTER FUNCTION my_function(integer) NO DEPENDS ON EXTENSION hstore"#, + ]); +} diff --git a/tests/test_alter_group.rs b/tests/test_alter_group.rs new file mode 100644 index 0000000..e3ececa --- /dev/null +++ b/tests/test_alter_group.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_group_parses() { + run_cases(&[ + r#"ALTER GROUP old_group RENAME TO new_group"#, + r#"ALTER GROUP my_group ADD USER alice, bob"#, + r#"ALTER GROUP my_group DROP USER alice, bob"#, + ]); +} diff --git a/tests/test_alter_index.rs b/tests/test_alter_index.rs new file mode 100644 index 0000000..2022837 --- /dev/null +++ b/tests/test_alter_index.rs @@ -0,0 +1,18 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_index_parses() { + run_cases(&[ + r#"ALTER INDEX IF EXISTS my_index RENAME TO my_index_new"#, + r#"ALTER INDEX ALL IN TABLESPACE oldspace OWNED BY app_user SET TABLESPACE newspace NOWAIT"#, + r#"ALTER INDEX my_index ATTACH PARTITION my_index_part"#, + r#"ALTER INDEX my_index DEPENDS ON EXTENSION hstore"#, + r#"ALTER INDEX my_index NO DEPENDS ON EXTENSION hstore"#, + r#"ALTER INDEX my_index SET TABLESPACE fastspace"#, + r#"ALTER INDEX my_index SET (fillfactor = 80, deduplicate_items = off)"#, + r#"ALTER INDEX my_index RESET (fillfactor)"#, + r#"ALTER INDEX my_index ALTER COLUMN 1 SET STATISTICS 100"#, + ]); +} diff --git a/tests/test_alter_language.rs b/tests/test_alter_language.rs new file mode 100644 index 0000000..ebed319 --- /dev/null +++ b/tests/test_alter_language.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_language_parses() { + run_cases(&[ + r#"ALTER LANGUAGE plsample RENAME TO plsample2"#, + r#"ALTER LANGUAGE plsample OWNER TO CURRENT_USER"#, + ]); +} diff --git a/tests/test_alter_large_object.rs b/tests/test_alter_large_object.rs new file mode 100644 index 0000000..359204c --- /dev/null +++ b/tests/test_alter_large_object.rs @@ -0,0 +1,8 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_large_object_parses() { + run_cases(&[r#"ALTER LARGE OBJECT 12345 OWNER TO CURRENT_USER"#]); +} diff --git a/tests/test_alter_materialized_view.rs b/tests/test_alter_materialized_view.rs new file mode 100644 index 0000000..e4f573c --- /dev/null +++ b/tests/test_alter_materialized_view.rs @@ -0,0 +1,22 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_materialized_view_parses() { + run_cases(&[ + r#"ALTER MATERIALIZED VIEW IF EXISTS mv RENAME TO mv_new"#, + r#"ALTER MATERIALIZED VIEW mv ALTER COLUMN col SET STATISTICS 100"#, + r#"ALTER MATERIALIZED VIEW mv ALTER COLUMN col SET (n_distinct = 10)"#, + r#"ALTER MATERIALIZED VIEW mv ALTER COLUMN col RESET (n_distinct)"#, + r#"ALTER MATERIALIZED VIEW mv CLUSTER ON mv_idx"#, + r#"ALTER MATERIALIZED VIEW mv SET WITHOUT CLUSTER"#, + r#"ALTER MATERIALIZED VIEW mv SET ACCESS METHOD heap"#, + r#"ALTER MATERIALIZED VIEW mv SET TABLESPACE fastspace"#, + r#"ALTER MATERIALIZED VIEW mv SET (fillfactor = 80)"#, + r#"ALTER MATERIALIZED VIEW mv RESET (fillfactor)"#, + r#"ALTER MATERIALIZED VIEW mv OWNER TO CURRENT_USER"#, + r#"ALTER MATERIALIZED VIEW mv SET SCHEMA public"#, + r#"ALTER MATERIALIZED VIEW ALL IN TABLESPACE oldspace OWNED BY app_user SET TABLESPACE newspace NOWAIT"#, + ]); +} diff --git a/tests/test_alter_operator.rs b/tests/test_alter_operator.rs new file mode 100644 index 0000000..1d06e4f --- /dev/null +++ b/tests/test_alter_operator.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_operator_parses() { + run_cases(&[ + r#"ALTER OPERATOR +(integer, integer) OWNER TO CURRENT_USER"#, + r#"ALTER OPERATOR +(integer, integer) SET SCHEMA public"#, + r#"ALTER OPERATOR +(integer, integer) SET (RESTRICT = my_restrict, JOIN = my_join)"#, + ]); +} diff --git a/tests/test_alter_operator_class.rs b/tests/test_alter_operator_class.rs new file mode 100644 index 0000000..472ddc2 --- /dev/null +++ b/tests/test_alter_operator_class.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_operator_class_parses() { + run_cases(&[ + r#"ALTER OPERATOR CLASS my_opclass USING btree RENAME TO my_opclass_new"#, + r#"ALTER OPERATOR CLASS my_opclass USING btree OWNER TO CURRENT_USER"#, + r#"ALTER OPERATOR CLASS my_opclass USING btree SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_operator_family.rs b/tests/test_alter_operator_family.rs new file mode 100644 index 0000000..3ff49f0 --- /dev/null +++ b/tests/test_alter_operator_family.rs @@ -0,0 +1,14 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_operator_family_parses() { + run_cases(&[ + r#"ALTER OPERATOR FAMILY my_family USING btree ADD OPERATOR 1 =(integer, integer)"#, + r#"ALTER OPERATOR FAMILY my_family USING btree DROP OPERATOR 1 (integer, integer)"#, + r#"ALTER OPERATOR FAMILY my_family USING btree RENAME TO my_family_new"#, + r#"ALTER OPERATOR FAMILY my_family USING btree OWNER TO CURRENT_USER"#, + r#"ALTER OPERATOR FAMILY my_family USING btree SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_policy.rs b/tests/test_alter_policy.rs new file mode 100644 index 0000000..57fb229 --- /dev/null +++ b/tests/test_alter_policy.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_policy_parses() { + run_cases(&[ + r#"ALTER POLICY pol ON my_table RENAME TO pol_new"#, + r#"ALTER POLICY pol ON my_table TO app_user USING (tenant_id = 1) WITH CHECK (tenant_id = 1)"#, + ]); +} diff --git a/tests/test_alter_procedure.rs b/tests/test_alter_procedure.rs new file mode 100644 index 0000000..d8d0ebd --- /dev/null +++ b/tests/test_alter_procedure.rs @@ -0,0 +1,18 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_procedure_parses() { + run_cases(&[ + r#"ALTER PROCEDURE my_proc(integer) SECURITY DEFINER"#, + r#"ALTER PROCEDURE my_proc(integer) SECURITY INVOKER"#, + r#"ALTER PROCEDURE my_proc(integer) SET search_path TO public"#, + r#"ALTER PROCEDURE my_proc(integer) RESET ALL"#, + r#"ALTER PROCEDURE my_proc(integer) OWNER TO CURRENT_USER"#, + r#"ALTER PROCEDURE my_proc(integer) RENAME TO my_proc_new"#, + r#"ALTER PROCEDURE my_proc(integer) SET SCHEMA public"#, + r#"ALTER PROCEDURE my_proc(integer) DEPENDS ON EXTENSION hstore"#, + r#"ALTER PROCEDURE my_proc(integer) NO DEPENDS ON EXTENSION hstore"#, + ]); +} diff --git a/tests/test_alter_publication.rs b/tests/test_alter_publication.rs new file mode 100644 index 0000000..cb75602 --- /dev/null +++ b/tests/test_alter_publication.rs @@ -0,0 +1,18 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_publication_parses() { + run_cases(&[ + r#"ALTER PUBLICATION pub ADD TABLE users, orders"#, + r#"ALTER PUBLICATION pub SET TABLE users WHERE (id > 0)"#, + r#"ALTER PUBLICATION pub DROP TABLE users"#, + r#"ALTER PUBLICATION pub ADD TABLES IN SCHEMA public, sales"#, + r#"ALTER PUBLICATION pub SET TABLES IN SCHEMA public"#, + r#"ALTER PUBLICATION pub DROP TABLES IN SCHEMA sales"#, + r#"ALTER PUBLICATION pub SET (publish = 'insert, update', publish_via_partition_root = true)"#, + r#"ALTER PUBLICATION pub OWNER TO CURRENT_USER"#, + r#"ALTER PUBLICATION pub RENAME TO pub_new"#, + ]); +} diff --git a/tests/test_alter_role.rs b/tests/test_alter_role.rs new file mode 100644 index 0000000..421b228 --- /dev/null +++ b/tests/test_alter_role.rs @@ -0,0 +1,17 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_role_parses() { + run_cases(&[ + r#"ALTER ROLE app_user WITH LOGIN CREATEDB PASSWORD 'secret'"#, + r#"ALTER ROLE app_user RENAME TO app_user_new"#, + r#"ALTER ROLE app_user IN DATABASE mydb SET search_path TO public"#, + r#"ALTER ROLE app_user SET work_mem = '64MB'"#, + r#"ALTER ROLE app_user SET enable_seqscan FROM CURRENT"#, + r#"ALTER ROLE app_user RESET work_mem"#, + r#"ALTER ROLE app_user RESET ALL"#, + r#"ALTER ROLE ALL IN DATABASE mydb SET statement_timeout TO '5s'"#, + ]); +} diff --git a/tests/test_alter_routine.rs b/tests/test_alter_routine.rs new file mode 100644 index 0000000..b58be1c --- /dev/null +++ b/tests/test_alter_routine.rs @@ -0,0 +1,17 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_routine_parses() { + run_cases(&[ + r#"ALTER ROUTINE my_function(integer) IMMUTABLE PARALLEL SAFE COST 10"#, + r#"ALTER ROUTINE my_function(integer) SET search_path TO public"#, + r#"ALTER ROUTINE my_function(integer) RESET ALL"#, + r#"ALTER ROUTINE my_function(integer) OWNER TO CURRENT_USER"#, + r#"ALTER ROUTINE my_function(integer) RENAME TO my_function_new"#, + r#"ALTER ROUTINE my_function(integer) SET SCHEMA public"#, + r#"ALTER ROUTINE my_function(integer) DEPENDS ON EXTENSION hstore"#, + r#"ALTER ROUTINE my_function(integer) NO DEPENDS ON EXTENSION hstore"#, + ]); +} diff --git a/tests/test_alter_rule.rs b/tests/test_alter_rule.rs new file mode 100644 index 0000000..0c1f55c --- /dev/null +++ b/tests/test_alter_rule.rs @@ -0,0 +1,8 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_rule_parses() { + run_cases(&[r#"ALTER RULE my_rule ON my_table RENAME TO my_rule_new"#]); +} diff --git a/tests/test_alter_schema.rs b/tests/test_alter_schema.rs new file mode 100644 index 0000000..91d1a73 --- /dev/null +++ b/tests/test_alter_schema.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_schema_parses() { + run_cases(&[ + r#"ALTER SCHEMA old_schema RENAME TO new_schema"#, + r#"ALTER SCHEMA old_schema OWNER TO CURRENT_USER"#, + ]); +} diff --git a/tests/test_alter_sequence.rs b/tests/test_alter_sequence.rs new file mode 100644 index 0000000..001cacf --- /dev/null +++ b/tests/test_alter_sequence.rs @@ -0,0 +1,14 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_sequence_parses() { + run_cases(&[ + r#"ALTER SEQUENCE IF EXISTS seq AS bigint INCREMENT BY 2 MINVALUE 1 MAXVALUE 1000 START WITH 10 RESTART WITH 20 CACHE 5 CYCLE OWNED BY users.id"#, + r#"ALTER SEQUENCE seq NO MINVALUE NO MAXVALUE NO CYCLE OWNED BY NONE"#, + r#"ALTER SEQUENCE seq OWNER TO CURRENT_USER"#, + r#"ALTER SEQUENCE seq RENAME TO seq_new"#, + r#"ALTER SEQUENCE seq SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_server.rs b/tests/test_alter_server.rs new file mode 100644 index 0000000..fd9eecf --- /dev/null +++ b/tests/test_alter_server.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_server_parses() { + run_cases(&[ + r#"ALTER SERVER my_server VERSION '2.0' OPTIONS (ADD host 'localhost', SET port '5432', DROP oldopt)"#, + r#"ALTER SERVER my_server OWNER TO CURRENT_USER"#, + r#"ALTER SERVER my_server RENAME TO my_server_new"#, + ]); +} diff --git a/tests/test_alter_statistics.rs b/tests/test_alter_statistics.rs new file mode 100644 index 0000000..5979166 --- /dev/null +++ b/tests/test_alter_statistics.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_statistics_parses() { + run_cases(&[ + r#"ALTER STATISTICS stats SET STATISTICS 1000"#, + r#"ALTER STATISTICS stats OWNER TO CURRENT_USER"#, + r#"ALTER STATISTICS stats RENAME TO stats_new"#, + r#"ALTER STATISTICS stats SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_subscription.rs b/tests/test_alter_subscription.rs new file mode 100644 index 0000000..aabd367 --- /dev/null +++ b/tests/test_alter_subscription.rs @@ -0,0 +1,20 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_subscription_parses() { + run_cases(&[ + r#"ALTER SUBSCRIPTION sub CONNECTION 'host=localhost dbname=postgres'"#, + r#"ALTER SUBSCRIPTION sub SET PUBLICATION pub1, pub2 WITH (copy_data = false)"#, + r#"ALTER SUBSCRIPTION sub ADD PUBLICATION pub3 WITH (copy_data = true)"#, + r#"ALTER SUBSCRIPTION sub DROP PUBLICATION pub3 WITH (refresh = false)"#, + r#"ALTER SUBSCRIPTION sub REFRESH PUBLICATION WITH (copy_data = false)"#, + r#"ALTER SUBSCRIPTION sub ENABLE"#, + r#"ALTER SUBSCRIPTION sub DISABLE"#, + r#"ALTER SUBSCRIPTION sub SET (slot_name = 'slot1', synchronous_commit = off)"#, + r#"ALTER SUBSCRIPTION sub SKIP (lsn = '0/16B6C50')"#, + r#"ALTER SUBSCRIPTION sub OWNER TO CURRENT_USER"#, + r#"ALTER SUBSCRIPTION sub RENAME TO sub_new"#, + ]); +} diff --git a/tests/test_alter_system.rs b/tests/test_alter_system.rs new file mode 100644 index 0000000..8c6049c --- /dev/null +++ b/tests/test_alter_system.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_system_parses() { + run_cases(&[ + r#"ALTER SYSTEM SET work_mem TO '64MB'"#, + r#"ALTER SYSTEM SET work_mem = '64MB'"#, + r#"ALTER SYSTEM RESET work_mem"#, + r#"ALTER SYSTEM RESET ALL"#, + ]); +} diff --git a/tests/test_alter_table.rs b/tests/test_alter_table.rs new file mode 100644 index 0000000..789e982 --- /dev/null +++ b/tests/test_alter_table.rs @@ -0,0 +1,71 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_table_parses() { + run_cases(&[ + r#"ALTER TABLE IF EXISTS ONLY my_table ADD COLUMN IF NOT EXISTS new_column integer COLLATE "C" DEFAULT 0"#, + r#"ALTER TABLE my_table DROP COLUMN IF EXISTS old_column CASCADE"#, + r#"ALTER TABLE my_table ALTER COLUMN id TYPE bigint USING id::bigint"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET DEFAULT 1"#, + r#"ALTER TABLE my_table ALTER COLUMN id DROP DEFAULT"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET NOT NULL"#, + r#"ALTER TABLE my_table ALTER COLUMN id DROP NOT NULL"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET EXPRESSION AS (id + 1)"#, + r#"ALTER TABLE my_table ALTER COLUMN id DROP EXPRESSION IF EXISTS"#, + r#"ALTER TABLE my_table ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (START WITH 1)"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET GENERATED BY DEFAULT"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET INCREMENT BY 2"#, + r#"ALTER TABLE my_table ALTER COLUMN id RESTART WITH 10"#, + r#"ALTER TABLE my_table ALTER COLUMN id DROP IDENTITY IF EXISTS"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET STATISTICS DEFAULT"#, + r#"ALTER TABLE my_table ALTER COLUMN id SET (n_distinct = 10)"#, + r#"ALTER TABLE my_table ALTER COLUMN id RESET (n_distinct)"#, + r#"ALTER TABLE my_table ALTER COLUMN body SET STORAGE EXTENDED"#, + r#"ALTER TABLE my_table ALTER COLUMN body SET COMPRESSION pglz"#, + r#"ALTER TABLE my_table ADD CONSTRAINT nn NOT NULL id NO INHERIT"#, + r#"ALTER TABLE my_table ADD CONSTRAINT chk CHECK (id > 0) NOT VALID"#, + r#"ALTER TABLE my_table ADD CONSTRAINT uq UNIQUE NULLS NOT DISTINCT (id) INCLUDE (body) WITH (fillfactor = 80) USING INDEX TABLESPACE fastspace"#, + r#"ALTER TABLE my_table ADD CONSTRAINT pk PRIMARY KEY USING INDEX my_index"#, + r#"ALTER TABLE my_table ALTER CONSTRAINT fk DEFERRABLE INITIALLY DEFERRED ENFORCED"#, + r#"ALTER TABLE my_table ALTER CONSTRAINT nn NO INHERIT"#, + r#"ALTER TABLE my_table VALIDATE CONSTRAINT chk"#, + r#"ALTER TABLE my_table DROP CONSTRAINT IF EXISTS chk RESTRICT"#, + r#"ALTER TABLE my_table DISABLE TRIGGER USER"#, + r#"ALTER TABLE my_table ENABLE REPLICA TRIGGER trg"#, + r#"ALTER TABLE my_table DISABLE RULE rewrite_rule"#, + r#"ALTER TABLE my_table ENABLE ALWAYS RULE rewrite_rule"#, + r#"ALTER TABLE my_table DISABLE ROW LEVEL SECURITY"#, + r#"ALTER TABLE my_table ENABLE ROW LEVEL SECURITY"#, + r#"ALTER TABLE my_table FORCE ROW LEVEL SECURITY"#, + r#"ALTER TABLE my_table NO FORCE ROW LEVEL SECURITY"#, + r#"ALTER TABLE my_table CLUSTER ON my_index"#, + r#"ALTER TABLE my_table SET WITHOUT CLUSTER"#, + r#"ALTER TABLE my_table SET WITHOUT OIDS"#, + r#"ALTER TABLE my_table SET ACCESS METHOD DEFAULT"#, + r#"ALTER TABLE my_table SET TABLESPACE fastspace"#, + r#"ALTER TABLE my_table SET LOGGED"#, + r#"ALTER TABLE my_table SET UNLOGGED"#, + r#"ALTER TABLE my_table SET (fillfactor = 80)"#, + r#"ALTER TABLE my_table RESET (fillfactor)"#, + r#"ALTER TABLE my_table INHERIT parent_table"#, + r#"ALTER TABLE my_table NO INHERIT parent_table"#, + r#"ALTER TABLE my_table OF my_type"#, + r#"ALTER TABLE my_table NOT OF"#, + r#"ALTER TABLE my_table OWNER TO CURRENT_USER"#, + r#"ALTER TABLE my_table REPLICA IDENTITY DEFAULT"#, + r#"ALTER TABLE my_table REPLICA IDENTITY USING INDEX my_index"#, + r#"ALTER TABLE my_table RENAME COLUMN old_column TO new_column"#, + r#"ALTER TABLE my_table RENAME CONSTRAINT old_constraint TO new_constraint"#, + r#"ALTER TABLE my_table RENAME TO my_table_new"#, + r#"ALTER TABLE my_table SET SCHEMA archive"#, + r#"ALTER TABLE ALL IN TABLESPACE oldspace OWNED BY app_user SET TABLESPACE newspace NOWAIT"#, + r#"ALTER TABLE parent ATTACH PARTITION child FOR VALUES IN (1, 2)"#, + r#"ALTER TABLE parent ATTACH PARTITION child FOR VALUES FROM (1, MINVALUE) TO (10, MAXVALUE)"#, + r#"ALTER TABLE parent ATTACH PARTITION child FOR VALUES WITH (MODULUS 4, REMAINDER 1)"#, + r#"ALTER TABLE parent ATTACH PARTITION child DEFAULT"#, + r#"ALTER TABLE parent DETACH PARTITION child CONCURRENTLY"#, + r#"ALTER TABLE parent DETACH PARTITION child FINALIZE"#, + ]); +} diff --git a/tests/test_alter_tablespace.rs b/tests/test_alter_tablespace.rs new file mode 100644 index 0000000..b95458f --- /dev/null +++ b/tests/test_alter_tablespace.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_tablespace_parses() { + run_cases(&[ + r#"ALTER TABLESPACE fastspace RENAME TO slowspace"#, + r#"ALTER TABLESPACE fastspace OWNER TO CURRENT_USER"#, + r#"ALTER TABLESPACE fastspace SET (random_page_cost = 1.1, seq_page_cost = 1.0)"#, + r#"ALTER TABLESPACE fastspace RESET (random_page_cost)"#, + ]); +} diff --git a/tests/test_alter_text_search_configuration.rs b/tests/test_alter_text_search_configuration.rs new file mode 100644 index 0000000..5c04230 --- /dev/null +++ b/tests/test_alter_text_search_configuration.rs @@ -0,0 +1,79 @@ +mod common; + +use common::run_parse_debug_case as run_case; +use pg_raw_parse::{Node, parse}; + +#[test] +fn alter_text_search_configuration_add_mapping() { + run_case(r#"ALTER TEXT SEARCH CONFIGURATION my_config ADD MAPPING FOR asciiword WITH simple"#); +} + +#[test] +fn alter_text_search_configuration_alter_mapping_for_tokens() { + run_case( + r#"ALTER TEXT SEARCH CONFIGURATION my_config ALTER MAPPING FOR asciiword WITH english_stem"#, + ); +} + +#[test] +fn alter_text_search_configuration_replace_mapping_dictionary() { + run_case( + r#"ALTER TEXT SEARCH CONFIGURATION my_config ALTER MAPPING REPLACE simple WITH english_stem"#, + ); +} + +#[test] +fn alter_text_search_configuration_replace_mapping_for_tokens() { + run_case( + r#"ALTER TEXT SEARCH CONFIGURATION my_config ALTER MAPPING FOR asciiword REPLACE simple WITH english_stem"#, + ); +} + +#[test] +fn alter_text_search_configuration_drop_mapping() { + run_case(r#"ALTER TEXT SEARCH CONFIGURATION my_config DROP MAPPING IF EXISTS FOR asciiword"#); +} + +#[test] +fn alter_text_search_configuration_rename() { + run_case(r#"ALTER TEXT SEARCH CONFIGURATION my_config RENAME TO my_config_new"#); +} + +#[test] +fn alter_text_search_configuration_owner() { + run_case(r#"ALTER TEXT SEARCH CONFIGURATION my_config OWNER TO CURRENT_USER"#); +} + +#[test] +fn alter_text_search_configuration_set_schema() { + run_case(r#"ALTER TEXT SEARCH CONFIGURATION my_config SET SCHEMA public"#); +} + +#[test] +fn alter_text_search_configuration_mapping_fields_are_node_lists() { + let ast = + parse(r#"ALTER TEXT SEARCH CONFIGURATION my_config ADD MAPPING FOR asciiword WITH simple"#) + .unwrap(); + + let Node::AlterTSConfigurationStmt(stmt) = ast.stmts().next().unwrap() else { + panic!("expected AlterTSConfigurationStmt"); + }; + + // tokentype() is a generic NodeList. For named token types, entries are + // String nodes. + let token_type = stmt.tokentype().first().expect("expected token type"); + assert_eq!(token_type.as_str(), Some("asciiword")); + + // Dictionaries are represented as a list of qualified-name lists. For the + // unqualified dictionary `simple`, dicts() contains one nested NodeList, + // and that nested list contains one String node. + let dictionary_name = stmt + .dicts() + .first() + .expect("expected dictionary") + .expect_node_list(); + assert_eq!( + dictionary_name.first().and_then(Node::as_str), + Some("simple") + ); +} diff --git a/tests/test_alter_text_search_dictionary.rs b/tests/test_alter_text_search_dictionary.rs new file mode 100644 index 0000000..3ef5bff --- /dev/null +++ b/tests/test_alter_text_search_dictionary.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_text_search_dictionary_parses() { + run_cases(&[ + r#"ALTER TEXT SEARCH DICTIONARY my_dict (StopWords = english)"#, + r#"ALTER TEXT SEARCH DICTIONARY my_dict RENAME TO my_dict_new"#, + r#"ALTER TEXT SEARCH DICTIONARY my_dict OWNER TO CURRENT_USER"#, + r#"ALTER TEXT SEARCH DICTIONARY my_dict SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_text_search_parser.rs b/tests/test_alter_text_search_parser.rs new file mode 100644 index 0000000..4f0a64d --- /dev/null +++ b/tests/test_alter_text_search_parser.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_text_search_parser_parses() { + run_cases(&[ + r#"ALTER TEXT SEARCH PARSER my_parser RENAME TO my_parser_new"#, + r#"ALTER TEXT SEARCH PARSER my_parser SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_text_search_template.rs b/tests/test_alter_text_search_template.rs new file mode 100644 index 0000000..5e0f05a --- /dev/null +++ b/tests/test_alter_text_search_template.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_text_search_template_parses() { + run_cases(&[ + r#"ALTER TEXT SEARCH TEMPLATE my_template RENAME TO my_template_new"#, + r#"ALTER TEXT SEARCH TEMPLATE my_template SET SCHEMA public"#, + ]); +} diff --git a/tests/test_alter_trigger.rs b/tests/test_alter_trigger.rs new file mode 100644 index 0000000..c873834 --- /dev/null +++ b/tests/test_alter_trigger.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_trigger_parses() { + run_cases(&[ + r#"ALTER TRIGGER trg ON my_table RENAME TO trg_new"#, + r#"ALTER TRIGGER trg ON my_table DEPENDS ON EXTENSION hstore"#, + r#"ALTER TRIGGER trg ON my_table NO DEPENDS ON EXTENSION hstore"#, + ]); +} diff --git a/tests/test_alter_type.rs b/tests/test_alter_type.rs new file mode 100644 index 0000000..547132c --- /dev/null +++ b/tests/test_alter_type.rs @@ -0,0 +1,20 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_type_parses() { + run_cases(&[ + r#"ALTER TYPE my_type OWNER TO CURRENT_USER"#, + r#"ALTER TYPE my_type RENAME TO my_type_new"#, + r#"ALTER TYPE my_type SET SCHEMA public"#, + r#"ALTER TYPE my_type RENAME ATTRIBUTE old_attr TO new_attr CASCADE"#, + r#"ALTER TYPE my_type ADD ATTRIBUTE new_attr integer COLLATE "C" RESTRICT"#, + r#"ALTER TYPE my_type DROP ATTRIBUTE IF EXISTS old_attr CASCADE"#, + r#"ALTER TYPE my_type ALTER ATTRIBUTE attr TYPE text COLLATE "C" CASCADE"#, + r#"ALTER TYPE mood ADD VALUE 'happy' BEFORE 'sad'"#, + r#"ALTER TYPE mood RENAME VALUE 'happy' TO 'joyful'"#, + r#"ALTER TYPE my_type SET (RECEIVE = my_receive, SEND = my_send)"#, + r#"ALTER TYPE my_type SET (STORAGE = extended)"#, + ]); +} diff --git a/tests/test_alter_user.rs b/tests/test_alter_user.rs new file mode 100644 index 0000000..f55d01e --- /dev/null +++ b/tests/test_alter_user.rs @@ -0,0 +1,15 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_user_parses() { + run_cases(&[ + r#"ALTER USER app_user WITH LOGIN PASSWORD 'secret'"#, + r#"ALTER USER app_user RENAME TO app_user_new"#, + r#"ALTER USER app_user SET search_path TO public"#, + r#"ALTER USER app_user IN DATABASE mydb SET work_mem TO '64MB'"#, + r#"ALTER USER app_user RESET work_mem"#, + r#"ALTER USER app_user RESET ALL"#, + ]); +} diff --git a/tests/test_alter_user_mapping.rs b/tests/test_alter_user_mapping.rs new file mode 100644 index 0000000..ee4a973 --- /dev/null +++ b/tests/test_alter_user_mapping.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_user_mapping_parses() { + run_cases(&[ + r#"ALTER USER MAPPING FOR app_user SERVER my_server OPTIONS (ADD user 'remote', SET password 'secret')"#, + r#"ALTER USER MAPPING FOR CURRENT_USER SERVER my_server OPTIONS (DROP password)"#, + r#"ALTER USER MAPPING FOR PUBLIC SERVER my_server OPTIONS (SET user 'public')"#, + ]); +} diff --git a/tests/test_alter_view.rs b/tests/test_alter_view.rs new file mode 100644 index 0000000..994bed0 --- /dev/null +++ b/tests/test_alter_view.rs @@ -0,0 +1,16 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn alter_view_parses() { + run_cases(&[ + r#"ALTER VIEW IF EXISTS v ALTER COLUMN col SET DEFAULT 1"#, + r#"ALTER VIEW v ALTER COLUMN col DROP DEFAULT"#, + r#"ALTER VIEW v OWNER TO CURRENT_USER"#, + r#"ALTER VIEW v RENAME TO v_new"#, + r#"ALTER VIEW v SET SCHEMA public"#, + r#"ALTER VIEW v SET (security_barrier = true, check_option = local)"#, + r#"ALTER VIEW v RESET (security_barrier)"#, + ]); +} diff --git a/tests/test_comment.rs b/tests/test_comment.rs new file mode 100644 index 0000000..9d3f841 --- /dev/null +++ b/tests/test_comment.rs @@ -0,0 +1,16 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn comment_parses() { + run_cases(&[ + r#"COMMENT ON TABLE my_table IS 'table comment'"#, + r#"COMMENT ON COLUMN my_table.id IS 'column comment'"#, + r#"COMMENT ON FUNCTION my_function(integer) IS 'function comment'"#, + r#"COMMENT ON OPERATOR +(integer, integer) IS 'operator comment'"#, + r#"COMMENT ON CONSTRAINT my_constraint ON my_table IS 'constraint comment'"#, + r#"COMMENT ON LARGE OBJECT 12345 IS 'large object comment'"#, + r#"COMMENT ON TABLE my_table IS NULL"#, + ]); +} diff --git a/tests/test_create_access_method.rs b/tests/test_create_access_method.rs new file mode 100644 index 0000000..f6a275d --- /dev/null +++ b/tests/test_create_access_method.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_access_method_parses() { + run_cases(&[ + r#"CREATE ACCESS METHOD my_am TYPE INDEX HANDLER my_handler"#, + r#"CREATE ACCESS METHOD my_table_am TYPE TABLE HANDLER my_table_handler"#, + ]); +} diff --git a/tests/test_create_aggregate.rs b/tests/test_create_aggregate.rs new file mode 100644 index 0000000..0d279f6 --- /dev/null +++ b/tests/test_create_aggregate.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_aggregate_parses() { + run_cases(&[ + r#"CREATE AGGREGATE my_sum(integer) (SFUNC = int4pl, STYPE = integer)"#, + r#"CREATE AGGREGATE my_sum(*) (SFUNC = int8inc, STYPE = bigint)"#, + r#"CREATE AGGREGATE my_sum(ORDER BY integer) (SFUNC = int4pl, STYPE = integer)"#, + r#"CREATE AGGREGATE my_hypothetical(integer ORDER BY integer) (SFUNC = int4pl, STYPE = integer, FINALFUNC = int4abs)"#, + ]); +} diff --git a/tests/test_create_cast.rs b/tests/test_create_cast.rs new file mode 100644 index 0000000..bd24225 --- /dev/null +++ b/tests/test_create_cast.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_cast_parses() { + run_cases(&[ + r#"CREATE CAST (integer AS bigint) WITHOUT FUNCTION AS IMPLICIT"#, + r#"CREATE CAST (integer AS text) WITH FUNCTION int4out(integer) AS ASSIGNMENT"#, + r#"CREATE CAST (my_type AS text) WITH INOUT"#, + ]); +} diff --git a/tests/test_create_collation.rs b/tests/test_create_collation.rs new file mode 100644 index 0000000..72c5096 --- /dev/null +++ b/tests/test_create_collation.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_collation_parses() { + run_cases(&[ + r#"CREATE COLLATION my_collation (provider = libc, locale = 'C')"#, + r#"CREATE COLLATION IF NOT EXISTS my_collation FROM "C""#, + ]); +} diff --git a/tests/test_create_conversion.rs b/tests/test_create_conversion.rs new file mode 100644 index 0000000..e44d517 --- /dev/null +++ b/tests/test_create_conversion.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_conversion_parses() { + run_cases(&[ + r#"CREATE DEFAULT CONVERSION my_conversion FOR 'UTF8' TO 'LATIN1' FROM my_convert_function"#, + r#"CREATE CONVERSION my_conversion FOR 'LATIN1' TO 'UTF8' FROM my_convert_function"#, + ]); +} diff --git a/tests/test_create_database.rs b/tests/test_create_database.rs new file mode 100644 index 0000000..ff9405a --- /dev/null +++ b/tests/test_create_database.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_database_parses() { + run_cases(&[ + r#"CREATE DATABASE mydb"#, + r#"CREATE DATABASE mydb WITH OWNER = app_user TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'C' TABLESPACE = pg_default ALLOW_CONNECTIONS = true CONNECTION LIMIT = 10 IS_TEMPLATE = false"#, + r#"CREATE DATABASE mydb STRATEGY = WAL_LOG"#, + ]); +} diff --git a/tests/test_create_domain.rs b/tests/test_create_domain.rs new file mode 100644 index 0000000..651dbe7 --- /dev/null +++ b/tests/test_create_domain.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_domain_parses() { + run_cases(&[ + r#"CREATE DOMAIN email_domain AS text CHECK (VALUE LIKE '%@%')"#, + r#"CREATE DOMAIN email_domain AS text COLLATE "C" DEFAULT 'x@example.com' NOT NULL CONSTRAINT email_check CHECK (VALUE LIKE '%@%')"#, + ]); +} diff --git a/tests/test_create_event_trigger.rs b/tests/test_create_event_trigger.rs new file mode 100644 index 0000000..0441efc --- /dev/null +++ b/tests/test_create_event_trigger.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_event_trigger_parses() { + run_cases(&[ + r#"CREATE EVENT TRIGGER trg ON ddl_command_start EXECUTE FUNCTION my_function()"#, + r#"CREATE EVENT TRIGGER trg ON sql_drop WHEN TAG IN ('DROP TABLE', 'DROP VIEW') EXECUTE FUNCTION my_function()"#, + ]); +} diff --git a/tests/test_create_extension.rs b/tests/test_create_extension.rs new file mode 100644 index 0000000..9d39a69 --- /dev/null +++ b/tests/test_create_extension.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_extension_parses() { + run_cases(&[ + r#"CREATE EXTENSION hstore"#, + r#"CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public VERSION '1.8' CASCADE"#, + ]); +} diff --git a/tests/test_create_foreign_data_wrapper.rs b/tests/test_create_foreign_data_wrapper.rs new file mode 100644 index 0000000..e6cf948 --- /dev/null +++ b/tests/test_create_foreign_data_wrapper.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_foreign_data_wrapper_parses() { + run_cases(&[ + r#"CREATE FOREIGN DATA WRAPPER my_fdw HANDLER my_handler VALIDATOR my_validator OPTIONS (host 'localhost')"#, + r#"CREATE FOREIGN DATA WRAPPER my_fdw NO HANDLER NO VALIDATOR"#, + ]); +} diff --git a/tests/test_create_foreign_table.rs b/tests/test_create_foreign_table.rs new file mode 100644 index 0000000..46f858d --- /dev/null +++ b/tests/test_create_foreign_table.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_foreign_table_parses() { + run_cases(&[ + r#"CREATE FOREIGN TABLE ft (id integer OPTIONS (column_name 'id'), body text COLLATE "C" NOT NULL) SERVER my_server OPTIONS (schema_name 'public', table_name 'remote')"#, + r#"CREATE FOREIGN TABLE IF NOT EXISTS ft PARTITION OF parent FOR VALUES IN (1) SERVER my_server"#, + r#"CREATE FOREIGN TABLE ft (LIKE source INCLUDING DEFAULTS, CHECK (id > 0), FOREIGN KEY (id) REFERENCES other(id)) SERVER my_server"#, + ]); +} diff --git a/tests/test_create_function.rs b/tests/test_create_function.rs new file mode 100644 index 0000000..36a5a6a --- /dev/null +++ b/tests/test_create_function.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_function_parses() { + run_cases(&[ + r#"CREATE FUNCTION my_function() RETURNS integer LANGUAGE SQL AS 'SELECT 1'"#, + r#"CREATE OR REPLACE FUNCTION my_function(arg integer DEFAULT 1) RETURNS TABLE (value integer) LANGUAGE SQL STABLE AS 'SELECT arg'"#, + r#"CREATE FUNCTION my_function(IN arg integer, OUT value integer) RETURNS integer LANGUAGE SQL STRICT SECURITY DEFINER COST 5 ROWS 1 SET search_path TO public AS 'SELECT arg'"#, + ]); +} diff --git a/tests/test_create_group.rs b/tests/test_create_group.rs new file mode 100644 index 0000000..e1de69e --- /dev/null +++ b/tests/test_create_group.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_group_parses() { + run_cases(&[ + r#"CREATE GROUP my_group"#, + r#"CREATE GROUP my_group WITH USER alice, bob"#, + ]); +} diff --git a/tests/test_create_index.rs b/tests/test_create_index.rs new file mode 100644 index 0000000..f03e65a --- /dev/null +++ b/tests/test_create_index.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_index_parses() { + run_cases(&[ + r#"CREATE INDEX my_index ON my_table (id)"#, + r#"CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS my_index ON ONLY my_table USING btree (id COLLATE "C" int4_ops ASC NULLS LAST, (lower(name)) DESC) INCLUDE (email) NULLS NOT DISTINCT WITH (fillfactor = 80) TABLESPACE fastspace WHERE id > 0"#, + ]); +} diff --git a/tests/test_create_language.rs b/tests/test_create_language.rs new file mode 100644 index 0000000..a1bc51b --- /dev/null +++ b/tests/test_create_language.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_language_parses() { + run_cases(&[ + r#"CREATE LANGUAGE plsample"#, + r#"CREATE OR REPLACE TRUSTED PROCEDURAL LANGUAGE plsample HANDLER my_handler INLINE my_inline VALIDATOR my_validator"#, + ]); +} diff --git a/tests/test_create_materialized_view.rs b/tests/test_create_materialized_view.rs new file mode 100644 index 0000000..1a975f7 --- /dev/null +++ b/tests/test_create_materialized_view.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_materialized_view_parses() { + run_cases(&[ + r#"CREATE MATERIALIZED VIEW mv AS SELECT 1 AS col"#, + r#"CREATE MATERIALIZED VIEW IF NOT EXISTS mv (col) USING heap WITH (fillfactor = 80) TABLESPACE fastspace AS SELECT 1 WITH NO DATA"#, + ]); +} diff --git a/tests/test_create_operator.rs b/tests/test_create_operator.rs new file mode 100644 index 0000000..553244f --- /dev/null +++ b/tests/test_create_operator.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_operator_parses() { + run_cases(&[ + r#"CREATE OPERATOR + (FUNCTION = my_add, LEFTARG = integer, RIGHTARG = integer)"#, + r#"CREATE OPERATOR public.## (PROCEDURE = my_op, LEFTARG = NONE, RIGHTARG = integer, COMMUTATOR = OPERATOR(public.##), NEGATOR = OPERATOR(public.!!), RESTRICT = my_restrict, JOIN = my_join, HASHES, MERGES)"#, + ]); +} diff --git a/tests/test_create_operator_class.rs b/tests/test_create_operator_class.rs new file mode 100644 index 0000000..8d2edee --- /dev/null +++ b/tests/test_create_operator_class.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_operator_class_parses() { + run_cases(&[ + r#"CREATE OPERATOR CLASS my_opclass DEFAULT FOR TYPE integer USING btree AS OPERATOR 1 <(integer, integer), FUNCTION 1 btint4cmp(integer, integer), STORAGE integer"#, + r#"CREATE OPERATOR CLASS my_opclass FOR TYPE integer USING hash FAMILY my_family AS OPERATOR 1 =(integer, integer), FUNCTION 1 hashint4(integer)"#, + ]); +} diff --git a/tests/test_create_operator_family.rs b/tests/test_create_operator_family.rs new file mode 100644 index 0000000..ee868b6 --- /dev/null +++ b/tests/test_create_operator_family.rs @@ -0,0 +1,8 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_operator_family_parses() { + run_cases(&[r#"CREATE OPERATOR FAMILY my_family USING btree"#]); +} diff --git a/tests/test_create_policy.rs b/tests/test_create_policy.rs new file mode 100644 index 0000000..e871ac2 --- /dev/null +++ b/tests/test_create_policy.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_policy_parses() { + run_cases(&[ + r#"CREATE POLICY pol ON my_table"#, + r#"CREATE POLICY pol ON my_table AS RESTRICTIVE FOR UPDATE TO app_user USING (tenant_id = 1) WITH CHECK (tenant_id = 1)"#, + ]); +} diff --git a/tests/test_create_procedure.rs b/tests/test_create_procedure.rs new file mode 100644 index 0000000..6fc57bf --- /dev/null +++ b/tests/test_create_procedure.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_procedure_parses() { + run_cases(&[ + r#"CREATE PROCEDURE my_proc() LANGUAGE SQL AS 'SELECT 1'"#, + r#"CREATE OR REPLACE PROCEDURE my_proc(IN arg integer DEFAULT 1, OUT value integer) LANGUAGE SQL SECURITY DEFINER SET search_path TO public AS 'SELECT arg'"#, + ]); +} diff --git a/tests/test_create_publication.rs b/tests/test_create_publication.rs new file mode 100644 index 0000000..181efde --- /dev/null +++ b/tests/test_create_publication.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_publication_parses() { + run_cases(&[ + r#"CREATE PUBLICATION pub FOR ALL TABLES"#, + r#"CREATE PUBLICATION pub FOR TABLE users, ONLY orders (id, total) WHERE (total > 0), TABLES IN SCHEMA public WITH (publish = 'insert, update', publish_via_partition_root = true)"#, + r#"CREATE PUBLICATION pub WITH (publish = 'truncate')"#, + ]); +} diff --git a/tests/test_create_role.rs b/tests/test_create_role.rs new file mode 100644 index 0000000..86c5434 --- /dev/null +++ b/tests/test_create_role.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_role_parses() { + run_cases(&[ + r#"CREATE ROLE app_user"#, + r#"CREATE ROLE app_user WITH SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 5 PASSWORD 'secret' VALID UNTIL 'infinity' IN ROLE parent_role ROLE child_role ADMIN admin_role USER legacy_user SYSID 10"#, + ]); +} diff --git a/tests/test_create_rule.rs b/tests/test_create_rule.rs new file mode 100644 index 0000000..3826329 --- /dev/null +++ b/tests/test_create_rule.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_rule_parses() { + run_cases(&[ + r#"CREATE RULE my_rule AS ON INSERT TO my_table DO INSTEAD NOTHING"#, + r#"CREATE OR REPLACE RULE my_rule AS ON UPDATE TO my_table WHERE NEW.id > 0 DO ALSO (NOTIFY my_channel; SELECT 1)"#, + ]); +} diff --git a/tests/test_create_schema.rs b/tests/test_create_schema.rs new file mode 100644 index 0000000..57923f6 --- /dev/null +++ b/tests/test_create_schema.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_schema_parses() { + run_cases(&[ + r#"CREATE SCHEMA my_schema"#, + r#"CREATE SCHEMA IF NOT EXISTS AUTHORIZATION app_user"#, + r#"CREATE SCHEMA AUTHORIZATION app_user CREATE TABLE t (id integer) CREATE VIEW v AS SELECT 1 AS id"#, + r#"CREATE SCHEMA my_schema AUTHORIZATION app_user"#, + ]); +} diff --git a/tests/test_create_sequence.rs b/tests/test_create_sequence.rs new file mode 100644 index 0000000..15c5535 --- /dev/null +++ b/tests/test_create_sequence.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_sequence_parses() { + run_cases(&[ + r#"CREATE SEQUENCE seq"#, + r#"CREATE TEMPORARY SEQUENCE IF NOT EXISTS seq AS bigint INCREMENT BY 2 MINVALUE 1 MAXVALUE 100 START WITH 10 CACHE 5 CYCLE OWNED BY users.id"#, + ]); +} diff --git a/tests/test_create_server.rs b/tests/test_create_server.rs new file mode 100644 index 0000000..71e6897 --- /dev/null +++ b/tests/test_create_server.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_server_parses() { + run_cases(&[ + r#"CREATE SERVER my_server FOREIGN DATA WRAPPER my_fdw"#, + r#"CREATE SERVER IF NOT EXISTS my_server TYPE 'postgres' VERSION '18' FOREIGN DATA WRAPPER my_fdw OPTIONS (host 'localhost', dbname 'postgres')"#, + ]); +} diff --git a/tests/test_create_statistics.rs b/tests/test_create_statistics.rs new file mode 100644 index 0000000..7269e71 --- /dev/null +++ b/tests/test_create_statistics.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_statistics_parses() { + run_cases(&[ + r#"CREATE STATISTICS stats ON id, name FROM users"#, + r#"CREATE STATISTICS IF NOT EXISTS stats (dependencies, ndistinct, mcv) ON id, lower(name) FROM users"#, + ]); +} diff --git a/tests/test_create_subscription.rs b/tests/test_create_subscription.rs new file mode 100644 index 0000000..450ac71 --- /dev/null +++ b/tests/test_create_subscription.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_subscription_parses() { + run_cases(&[ + r#"CREATE SUBSCRIPTION sub CONNECTION 'host=localhost dbname=postgres' PUBLICATION pub"#, + r#"CREATE SUBSCRIPTION sub CONNECTION 'host=localhost dbname=postgres' PUBLICATION pub1, pub2 WITH (copy_data = false, create_slot = false)"#, + ]); +} diff --git a/tests/test_create_table.rs b/tests/test_create_table.rs new file mode 100644 index 0000000..a918c65 --- /dev/null +++ b/tests/test_create_table.rs @@ -0,0 +1,239 @@ +mod common; + +use common::{run_parse_debug_case as run_test, run_parse_debug_cases as run_cases}; +use pg_raw_parse::{Node, parse}; + +#[test] +fn create_table_prefix_variations() { + run_cases(&[ + "CREATE TABLE users ()", + "CREATE TABLE public.users ()", + "CREATE TEMP TABLE users ()", + "CREATE TEMPORARY TABLE users ()", + "CREATE LOCAL TEMP TABLE users ()", + "CREATE LOCAL TEMPORARY TABLE users ()", + "CREATE GLOBAL TEMP TABLE users ()", + "CREATE GLOBAL TEMPORARY TABLE users ()", + "CREATE UNLOGGED TABLE users ()", + "CREATE TABLE IF NOT EXISTS users ()", + "CREATE TEMP TABLE IF NOT EXISTS users ()", + "CREATE UNLOGGED TABLE IF NOT EXISTS users ()", + ]); +} + +#[test] +fn create_table_column_variations() { + run_cases(&[ + "CREATE TABLE users (id integer)", + "CREATE TABLE users (id integer STORAGE PLAIN)", + "CREATE TABLE users (id integer STORAGE EXTERNAL)", + "CREATE TABLE users (id integer STORAGE EXTENDED)", + "CREATE TABLE users (id integer STORAGE MAIN)", + "CREATE TABLE users (id integer STORAGE DEFAULT)", + "CREATE TABLE users (body text COMPRESSION pglz)", + "CREATE TABLE users (name text COLLATE \"C\")", + "CREATE TABLE users (id integer DEFAULT 1)", + "CREATE TABLE users (id integer CONSTRAINT users_id_default DEFAULT 1)", + "CREATE TABLE users (id integer CONSTRAINT users_id_nn NOT NULL)", + "CREATE TABLE users (name text NULL)", + "CREATE TABLE users (email text UNIQUE)", + "CREATE TABLE users (email text UNIQUE NULLS DISTINCT)", + "CREATE TABLE users (email text UNIQUE NULLS NOT DISTINCT)", + "CREATE TABLE users (email text UNIQUE WITH (fillfactor = 70))", + "CREATE TABLE users (email text UNIQUE USING INDEX TABLESPACE fastspace)", + "CREATE TABLE users (id integer PRIMARY KEY)", + "CREATE TABLE users (id integer PRIMARY KEY WITH (fillfactor = 70))", + "CREATE TABLE users (id integer PRIMARY KEY USING INDEX TABLESPACE fastspace)", + "CREATE TABLE users (age integer CHECK (age > 0))", + "CREATE TABLE users (age integer CHECK (age > 0) NO INHERIT)", + "CREATE TABLE users (normalized text GENERATED ALWAYS AS (lower(name)) STORED)", + "CREATE TABLE users (account_id integer REFERENCES accounts)", + "CREATE TABLE users (account_id integer REFERENCES accounts (id))", + "CREATE TABLE users (account_id integer REFERENCES accounts (id) MATCH FULL)", + "CREATE TABLE users (account_id integer REFERENCES accounts (id) MATCH SIMPLE)", + "CREATE TABLE users (account_id integer REFERENCES accounts (id) ON DELETE CASCADE ON UPDATE SET NULL)", + "CREATE TABLE users (account_id integer REFERENCES accounts (id) DEFERRABLE INITIALLY DEFERRED)", + "CREATE TABLE users (account_id integer REFERENCES accounts (id) NOT DEFERRABLE INITIALLY IMMEDIATE)", + ]); +} + +#[test] +fn create_table_constraint_variations() { + run_cases(&[ + "CREATE TABLE users (id integer, email text, UNIQUE (email))", + "CREATE TABLE users (id integer, email text, UNIQUE NULLS DISTINCT (email))", + "CREATE TABLE users (id integer, email text, UNIQUE NULLS NOT DISTINCT (email))", + "CREATE TABLE users (id integer, email text, UNIQUE (email) INCLUDE (id))", + "CREATE TABLE users (id integer, email text, UNIQUE (email) WITH (fillfactor = 70))", + "CREATE TABLE users (id integer, email text, CONSTRAINT users_email_key UNIQUE (email) USING INDEX TABLESPACE fastspace)", + "CREATE TABLE users (id integer, PRIMARY KEY (id))", + "CREATE TABLE users (id integer, email text, PRIMARY KEY (id) INCLUDE (email))", + "CREATE TABLE users (id integer, PRIMARY KEY (id) WITH (fillfactor = 70))", + "CREATE TABLE users (id integer, CONSTRAINT users_pkey PRIMARY KEY (id) USING INDEX TABLESPACE fastspace)", + "CREATE TABLE users (age integer, CHECK (age > 0))", + "CREATE TABLE users (age integer, CHECK (age > 0) NO INHERIT)", + "CREATE TABLE users (room int4range, EXCLUDE USING gist (room WITH &&))", + "CREATE TABLE users (room int4range, during tstzrange, EXCLUDE USING gist (room WITH &&, during WITH &&) WHERE (room IS NOT NULL))", + "CREATE TABLE users (room int4range, EXCLUDE (room WITH &&) INCLUDE (room) WITH (fillfactor = 70) USING INDEX TABLESPACE fastspace)", + "CREATE TABLE users (account_id integer, FOREIGN KEY (account_id) REFERENCES accounts)", + "CREATE TABLE users (account_id integer, tenant_id integer, FOREIGN KEY (account_id, tenant_id) REFERENCES accounts (id, tenant_id))", + "CREATE TABLE users (account_id integer, FOREIGN KEY (account_id) REFERENCES accounts (id) MATCH FULL ON DELETE CASCADE ON UPDATE RESTRICT)", + "CREATE TABLE users (account_id integer, tenant_id integer, FOREIGN KEY (account_id, tenant_id) REFERENCES accounts (id, tenant_id) ON DELETE SET NULL (account_id) ON UPDATE SET DEFAULT)", + "CREATE TABLE users (account_id integer, FOREIGN KEY (account_id) REFERENCES accounts (id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED)", + "CREATE TABLE users (account_id integer, CONSTRAINT users_account_fk FOREIGN KEY (account_id) REFERENCES accounts (id) NOT DEFERRABLE INITIALLY IMMEDIATE)", + ]); +} + +#[test] +fn create_table_like_variations() { + run_cases(&[ + "CREATE TABLE users (LIKE old_users)", + "CREATE TABLE users (LIKE old_users INCLUDING COMMENTS)", + "CREATE TABLE users (LIKE old_users INCLUDING COMPRESSION)", + "CREATE TABLE users (LIKE old_users INCLUDING CONSTRAINTS)", + "CREATE TABLE users (LIKE old_users INCLUDING DEFAULTS)", + "CREATE TABLE users (LIKE old_users INCLUDING GENERATED)", + "CREATE TABLE users (LIKE old_users INCLUDING IDENTITY)", + "CREATE TABLE users (LIKE old_users INCLUDING INDEXES)", + "CREATE TABLE users (LIKE old_users INCLUDING STATISTICS)", + "CREATE TABLE users (LIKE old_users INCLUDING STORAGE)", + "CREATE TABLE users (LIKE old_users INCLUDING ALL)", + "CREATE TABLE users (LIKE old_users EXCLUDING COMMENTS)", + "CREATE TABLE users (LIKE old_users EXCLUDING COMPRESSION)", + "CREATE TABLE users (LIKE old_users EXCLUDING CONSTRAINTS)", + "CREATE TABLE users (LIKE old_users EXCLUDING DEFAULTS)", + "CREATE TABLE users (LIKE old_users EXCLUDING GENERATED)", + "CREATE TABLE users (LIKE old_users EXCLUDING IDENTITY)", + "CREATE TABLE users (LIKE old_users EXCLUDING INDEXES)", + "CREATE TABLE users (LIKE old_users EXCLUDING STATISTICS)", + "CREATE TABLE users (LIKE old_users EXCLUDING STORAGE)", + "CREATE TABLE users (LIKE old_users EXCLUDING ALL)", + "CREATE TABLE users (LIKE old_users INCLUDING DEFAULTS EXCLUDING INDEXES)", + "CREATE TABLE users (id integer, LIKE old_users INCLUDING DEFAULTS, email text)", + ]); +} + +#[test] +fn create_table_options_variations() { + run_cases(&[ + "CREATE TABLE users (id integer) INHERITS (base_users)", + "CREATE TABLE users (id integer) INHERITS (base_users, audited_rows)", + "CREATE TABLE users (id integer) PARTITION BY RANGE (created_at)", + "CREATE TABLE users (id integer) PARTITION BY RANGE ((lower(name)) COLLATE \"C\" text_ops)", + "CREATE TABLE users (id integer) PARTITION BY LIST (tenant_id)", + "CREATE TABLE users (id integer) PARTITION BY HASH (tenant_id)", + "CREATE TABLE users (id integer) USING heap", + "CREATE TABLE users (id integer) WITH (fillfactor = 70)", + "CREATE TABLE users (id integer) WITH (fillfactor)", + "CREATE TABLE users (id integer) WITHOUT OIDS", + "CREATE TEMP TABLE users (id integer) ON COMMIT PRESERVE ROWS", + "CREATE TEMP TABLE users (id integer) ON COMMIT DELETE ROWS", + "CREATE TEMP TABLE users (id integer) ON COMMIT DROP", + "CREATE TABLE users (id integer) TABLESPACE fastspace", + "CREATE TEMP TABLE users (id integer) INHERITS (base_users) WITHOUT OIDS ON COMMIT DELETE ROWS TABLESPACE fastspace", + ]); +} + +#[test] +fn create_typed_table_variations() { + run_cases(&[ + "CREATE TABLE users OF user_type", + "CREATE TABLE users OF user_type (id WITH OPTIONS NOT NULL)", + "CREATE TABLE users OF user_type (id WITH OPTIONS DEFAULT 1, CHECK (id > 0))", + "CREATE TABLE users OF user_type PARTITION BY HASH (id)", + "CREATE TABLE users OF user_type USING heap WITH (fillfactor = 70) TABLESPACE fastspace", + ]); +} + +#[test] +fn create_partition_table_variations() { + run_cases(&[ + "CREATE TABLE users_default PARTITION OF users DEFAULT", + "CREATE TABLE users_us PARTITION OF users FOR VALUES IN ('us', 'ca')", + "CREATE TABLE users_hash_0 PARTITION OF users FOR VALUES WITH (MODULUS 4, REMAINDER 0)", + "CREATE TABLE users_hash_0 PARTITION OF users (id WITH OPTIONS NOT NULL) FOR VALUES WITH (MODULUS 4, REMAINDER 0)", + "CREATE TABLE users_us PARTITION OF users FOR VALUES IN ('us', 'ca') PARTITION BY HASH (tenant_id)", + "CREATE TABLE users_us PARTITION OF users FOR VALUES IN ('us', 'ca') USING heap WITH (fillfactor = 70) TABLESPACE fastspace", + ]); +} + +#[test] +fn create_table_identity_variations() { + run_cases(&[ + "CREATE TABLE users (id integer GENERATED ALWAYS AS IDENTITY)", + "CREATE TABLE users (id integer GENERATED BY DEFAULT AS IDENTITY)", + "CREATE TABLE users (id integer GENERATED ALWAYS AS IDENTITY (START WITH 10 INCREMENT BY 2))", + ]); +} + +#[test] +fn create_table_exclude_element_variations() { + run_cases(&[ + "CREATE TABLE bookings (room int4range, EXCLUDE USING gist (room WITH &&))", + "CREATE TABLE bookings (room text, EXCLUDE USING gist (room COLLATE \"C\" text_ops WITH =))", + "CREATE TABLE bookings (room text, EXCLUDE USING gist ((lower(room)) WITH =))", + "CREATE TABLE bookings (room int, EXCLUDE USING btree (room ASC NULLS LAST WITH =))", + ]); +} + +#[test] +#[should_panic(expected = "MATCH PARTIAL not yet implemented")] +fn failing_create_table_column_reference_match_partial() { + run_test("CREATE TABLE users (account_id integer REFERENCES accounts (id) MATCH PARTIAL)"); +} + +#[test] +#[should_panic(expected = "MATCH PARTIAL not yet implemented")] +fn failing_create_table_table_reference_match_partial() { + run_test( + "CREATE TABLE users (account_id integer, FOREIGN KEY (account_id) REFERENCES accounts (id) MATCH PARTIAL ON DELETE SET NULL ON UPDATE SET DEFAULT)", + ); +} + +#[test] +fn create_table_column_not_null_no_inherit() { + run_test("CREATE TABLE users (id integer NOT NULL NO INHERIT)"); +} + +#[test] +fn create_table_partition_of_range_from_to() { + run_test( + "CREATE TABLE users_2024 PARTITION OF users FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')", + ); +} + +#[test] +fn create_table_partition_of_range_minvalue_maxvalue() { + run_test( + "CREATE TABLE users_2024 PARTITION OF users (id WITH OPTIONS NOT NULL) FOR VALUES FROM (MINVALUE) TO (MAXVALUE)", + ); +} + +#[test] +fn create_table_partition_of_range_with_partition_by() { + let ast = parse( + "CREATE TABLE users_2024 PARTITION OF users FOR VALUES FROM ('2024-01-01') TO ('2025-01-01') PARTITION BY HASH (tenant_id)", + ) + .unwrap(); + + // Range partition bounds are exposed through CreateStmt::partbound(). + // lowerdatums()/upperdatums() are generic NodeLists because raw parse + // trees can contain A_Const nodes for concrete values. + let Node::CreateStmt(stmt) = ast.stmts().next().unwrap() else { + panic!("expected CREATE TABLE statement"); + }; + let bounds = stmt.partbound().expect("expected partition bounds"); + let lower = bounds.lowerdatums().first().expect("expected lower bound"); + let upper = bounds.upperdatums().first().expect("expected upper bound"); + + assert!(matches!(lower, Node::A_Const(_))); + assert!(matches!(upper, Node::A_Const(_))); + assert!(!format!("{bounds:?}").is_empty()); +} + +#[test] +fn create_table_partition_of_range_with_options() { + run_test( + "CREATE TABLE users_2024 PARTITION OF users FOR VALUES FROM ('2024-01-01') TO ('2025-01-01') USING heap WITH (fillfactor = 70) TABLESPACE fastspace", + ); +} diff --git a/tests/test_create_table_as.rs b/tests/test_create_table_as.rs new file mode 100644 index 0000000..28b9483 --- /dev/null +++ b/tests/test_create_table_as.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_table_as_parses() { + run_cases(&[ + r#"CREATE TABLE new_table AS SELECT 1 AS id"#, + r#"CREATE TEMP TABLE IF NOT EXISTS new_table (id, name) USING heap WITH (fillfactor = 80) ON COMMIT DROP TABLESPACE fastspace AS SELECT 1 AS id, 'x' AS name WITH NO DATA"#, + ]); +} diff --git a/tests/test_create_tablespace.rs b/tests/test_create_tablespace.rs new file mode 100644 index 0000000..2379d07 --- /dev/null +++ b/tests/test_create_tablespace.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_tablespace_parses() { + run_cases(&[ + r#"CREATE TABLESPACE fastspace LOCATION '/tmp/fastspace'"#, + r#"CREATE TABLESPACE fastspace OWNER app_user LOCATION '/tmp/fastspace' WITH (random_page_cost = 1.1)"#, + ]); +} diff --git a/tests/test_create_text_search_configuration.rs b/tests/test_create_text_search_configuration.rs new file mode 100644 index 0000000..306f2fe --- /dev/null +++ b/tests/test_create_text_search_configuration.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_text_search_configuration_parses() { + run_cases(&[ + r#"CREATE TEXT SEARCH CONFIGURATION my_config (PARSER = default)"#, + r#"CREATE TEXT SEARCH CONFIGURATION my_config (COPY = pg_catalog.english)"#, + ]); +} diff --git a/tests/test_create_text_search_dictionary.rs b/tests/test_create_text_search_dictionary.rs new file mode 100644 index 0000000..53446fa --- /dev/null +++ b/tests/test_create_text_search_dictionary.rs @@ -0,0 +1,10 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_text_search_dictionary_parses() { + run_cases(&[ + r#"CREATE TEXT SEARCH DICTIONARY my_dict (TEMPLATE = simple, StopWords = english)"#, + ]); +} diff --git a/tests/test_create_text_search_parser.rs b/tests/test_create_text_search_parser.rs new file mode 100644 index 0000000..cf80e53 --- /dev/null +++ b/tests/test_create_text_search_parser.rs @@ -0,0 +1,10 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_text_search_parser_parses() { + run_cases(&[ + r#"CREATE TEXT SEARCH PARSER my_parser (START = prsd_start, GETTOKEN = prsd_nexttoken, END = prsd_end, LEXTYPES = prsd_lextype, HEADLINE = prsd_headline)"#, + ]); +} diff --git a/tests/test_create_text_search_template.rs b/tests/test_create_text_search_template.rs new file mode 100644 index 0000000..c22a47a --- /dev/null +++ b/tests/test_create_text_search_template.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_text_search_template_parses() { + run_cases(&[ + r#"CREATE TEXT SEARCH TEMPLATE my_template (INIT = dsimple_init, LEXIZE = dsimple_lexize)"#, + r#"CREATE TEXT SEARCH TEMPLATE my_template (LEXIZE = dsimple_lexize)"#, + ]); +} diff --git a/tests/test_create_transform.rs b/tests/test_create_transform.rs new file mode 100644 index 0000000..aa4b139 --- /dev/null +++ b/tests/test_create_transform.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_transform_parses() { + run_cases(&[ + r#"CREATE TRANSFORM FOR my_type LANGUAGE plsample (FROM SQL WITH FUNCTION my_from_sql(my_type), TO SQL WITH FUNCTION my_to_sql(internal))"#, + r#"CREATE OR REPLACE TRANSFORM FOR my_type LANGUAGE plsample (FROM SQL WITH FUNCTION my_from_sql(my_type))"#, + ]); +} diff --git a/tests/test_create_trigger.rs b/tests/test_create_trigger.rs new file mode 100644 index 0000000..62214cc --- /dev/null +++ b/tests/test_create_trigger.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_trigger_parses() { + run_cases(&[ + r#"CREATE TRIGGER trg BEFORE INSERT OR UPDATE OF name ON my_table FOR EACH ROW WHEN (NEW.id > 0) EXECUTE FUNCTION my_function()"#, + r#"CREATE OR REPLACE TRIGGER trg AFTER DELETE ON my_table FOR EACH ROW EXECUTE FUNCTION my_function()"#, + r#"CREATE CONSTRAINT TRIGGER trg AFTER DELETE ON my_table DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION my_function()"#, + r#"CREATE TRIGGER trg AFTER TRUNCATE ON my_table REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table FOR EACH STATEMENT EXECUTE PROCEDURE my_function()"#, + ]); +} diff --git a/tests/test_create_type.rs b/tests/test_create_type.rs new file mode 100644 index 0000000..313af1e --- /dev/null +++ b/tests/test_create_type.rs @@ -0,0 +1,14 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_type_parses() { + run_cases(&[ + r#"CREATE TYPE address AS (street text COLLATE "C", zip integer)"#, + r#"CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy')"#, + r#"CREATE TYPE inventory_item AS RANGE (SUBTYPE = integer, SUBTYPE_OPCLASS = int4_ops, COLLATION = "C", CANONICAL = my_canonical, SUBTYPE_DIFF = my_diff, MULTIRANGE_TYPE_NAME = inventory_item_multirange)"#, + r#"CREATE TYPE shell_type"#, + r#"CREATE TYPE my_base_type (INPUT = my_input, OUTPUT = my_output, RECEIVE = my_receive, SEND = my_send, INTERNALLENGTH = variable, PASSEDBYVALUE, ALIGNMENT = int4, STORAGE = extended, LIKE = integer, CATEGORY = 'U', PREFERRED = true, DEFAULT = '0', ELEMENT = integer, DELIMITER = ',', COLLATABLE = true)"#, + ]); +} diff --git a/tests/test_create_user.rs b/tests/test_create_user.rs new file mode 100644 index 0000000..1284b5b --- /dev/null +++ b/tests/test_create_user.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_user_parses() { + run_cases(&[ + r#"CREATE USER app_user"#, + r#"CREATE USER app_user WITH SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 5 PASSWORD 'secret' VALID UNTIL 'infinity' IN ROLE parent_role ROLE child_role ADMIN admin_role USER legacy_user SYSID 10"#, + ]); +} diff --git a/tests/test_create_user_mapping.rs b/tests/test_create_user_mapping.rs new file mode 100644 index 0000000..cc5532b --- /dev/null +++ b/tests/test_create_user_mapping.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_user_mapping_parses() { + run_cases(&[ + r#"CREATE USER MAPPING FOR app_user SERVER my_server OPTIONS (user 'remote', password 'secret')"#, + r#"CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER my_server OPTIONS (user 'current')"#, + r#"CREATE USER MAPPING FOR PUBLIC SERVER my_server"#, + ]); +} diff --git a/tests/test_create_view.rs b/tests/test_create_view.rs new file mode 100644 index 0000000..3694b3c --- /dev/null +++ b/tests/test_create_view.rs @@ -0,0 +1,13 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn create_view_parses() { + run_cases(&[ + r#"CREATE VIEW v AS SELECT 1 AS col"#, + r#"CREATE OR REPLACE TEMP RECURSIVE VIEW v (col) WITH (security_barrier = true, check_option = local) AS SELECT 1 AS col"#, + r#"CREATE OR REPLACE TEMP VIEW v (col) WITH (security_barrier = true) AS SELECT 1 AS col WITH LOCAL CHECK OPTION"#, + r#"CREATE VIEW v AS SELECT 1 AS col WITH CASCADED CHECK OPTION"#, + ]); +} diff --git a/tests/test_delete_grammar.rs b/tests/test_delete_grammar.rs new file mode 100644 index 0000000..00b7a27 --- /dev/null +++ b/tests/test_delete_grammar.rs @@ -0,0 +1,59 @@ +mod common; + +use common::run_parse_walk_cases as run_cases; + +#[test] +fn delete_target_variations() { + run_cases(&[ + "DELETE FROM users", + "DELETE FROM ONLY users", + "DELETE FROM users *", + "DELETE FROM users AS u", + "DELETE FROM users u", + "DELETE FROM public.users AS u", + ]); +} + +#[test] +fn delete_with_clause_variations() { + run_cases(&[ + "WITH stale AS (SELECT id FROM users WHERE active = false) DELETE FROM users USING stale WHERE users.id = stale.id", + "WITH RECURSIVE stale(id) AS (SELECT 1 UNION ALL SELECT id + 1 FROM stale WHERE id < 3) DELETE FROM users USING stale WHERE users.id = stale.id", + "WITH stale AS MATERIALIZED (SELECT id FROM users) DELETE FROM users USING stale WHERE users.id = stale.id", + "WITH stale AS NOT MATERIALIZED (SELECT id FROM users) DELETE FROM users USING stale WHERE users.id = stale.id", + ]); +} + +#[test] +fn delete_using_variations() { + run_cases(&[ + "DELETE FROM users USING orders WHERE users.id = orders.user_id", + "DELETE FROM users USING orders o, sessions s WHERE users.id = o.user_id AND users.id = s.user_id", + "DELETE FROM users USING ONLY archived_users au WHERE users.id = au.id", + "DELETE FROM users USING LATERAL (SELECT 1 AS id) s WHERE users.id = s.id", + "DELETE FROM users USING generate_series(1, 3) AS g(id) WHERE users.id = g.id", + "DELETE FROM users USING orders JOIN invoices ON orders.id = invoices.order_id WHERE users.id = orders.user_id", + ]); +} + +#[test] +fn delete_where_variations() { + run_cases(&[ + "DELETE FROM users WHERE id = 1", + "DELETE FROM users WHERE id = $1 AND active = $2", + "DELETE FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)", + "DELETE FROM users WHERE CURRENT OF user_cursor", + ]); +} + +#[test] +fn delete_returning_variations() { + run_cases(&[ + "DELETE FROM users RETURNING *", + "DELETE FROM users RETURNING id", + "DELETE FROM users RETURNING id AS deleted_id, email old_email", + "DELETE FROM users RETURNING WITH (OLD AS old_row) old_row.id", + "DELETE FROM users RETURNING WITH (NEW AS new_row) new_row.id", + "DELETE FROM users RETURNING WITH (OLD AS old_row, NEW AS new_row) old_row.id AS old_id, new_row.id AS new_id", + ]); +} diff --git a/tests/test_drop.rs b/tests/test_drop.rs new file mode 100644 index 0000000..717f440 --- /dev/null +++ b/tests/test_drop.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_parses() { + run_cases(&[ + r#"DROP TABLE IF EXISTS my_table"#, + r#"DROP TABLE IF EXISTS my_table, other_table CASCADE"#, + ]); +} diff --git a/tests/test_drop_access_method.rs b/tests/test_drop_access_method.rs new file mode 100644 index 0000000..fd319da --- /dev/null +++ b/tests/test_drop_access_method.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_access_method_parses() { + run_cases(&[ + r#"DROP ACCESS METHOD my_am"#, + r#"DROP ACCESS METHOD IF EXISTS my_am CASCADE"#, + ]); +} diff --git a/tests/test_drop_aggregate.rs b/tests/test_drop_aggregate.rs new file mode 100644 index 0000000..76525ab --- /dev/null +++ b/tests/test_drop_aggregate.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_aggregate_parses() { + run_cases(&[ + r#"DROP AGGREGATE my_sum(integer)"#, + r#"DROP AGGREGATE IF EXISTS my_sum(integer), my_sum(bigint) CASCADE"#, + ]); +} diff --git a/tests/test_drop_cast.rs b/tests/test_drop_cast.rs new file mode 100644 index 0000000..05a093f --- /dev/null +++ b/tests/test_drop_cast.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_cast_parses() { + run_cases(&[ + r#"DROP CAST (integer AS bigint)"#, + r#"DROP CAST IF EXISTS (integer AS bigint) CASCADE"#, + ]); +} diff --git a/tests/test_drop_collation.rs b/tests/test_drop_collation.rs new file mode 100644 index 0000000..3879a2c --- /dev/null +++ b/tests/test_drop_collation.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_collation_parses() { + run_cases(&[ + r#"DROP COLLATION my_collation"#, + r#"DROP COLLATION IF EXISTS my_collation, my_collation_old CASCADE"#, + r#"DROP COLLATION IF EXISTS my_collation RESTRICT"#, + ]); +} diff --git a/tests/test_drop_conversion.rs b/tests/test_drop_conversion.rs new file mode 100644 index 0000000..43d15f4 --- /dev/null +++ b/tests/test_drop_conversion.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_conversion_parses() { + run_cases(&[ + r#"DROP CONVERSION my_conversion"#, + r#"DROP CONVERSION IF EXISTS my_conversion, my_conversion_old CASCADE"#, + r#"DROP CONVERSION IF EXISTS my_conversion RESTRICT"#, + ]); +} diff --git a/tests/test_drop_database.rs b/tests/test_drop_database.rs new file mode 100644 index 0000000..ad93bd7 --- /dev/null +++ b/tests/test_drop_database.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_database_parses() { + run_cases(&[ + r#"DROP DATABASE mydb"#, + r#"DROP DATABASE IF EXISTS mydb WITH (FORCE)"#, + ]); +} diff --git a/tests/test_drop_domain.rs b/tests/test_drop_domain.rs new file mode 100644 index 0000000..ce76f55 --- /dev/null +++ b/tests/test_drop_domain.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_domain_parses() { + run_cases(&[ + r#"DROP DOMAIN email_domain"#, + r#"DROP DOMAIN IF EXISTS email_domain, email_domain_old CASCADE"#, + r#"DROP DOMAIN IF EXISTS email_domain RESTRICT"#, + ]); +} diff --git a/tests/test_drop_event_trigger.rs b/tests/test_drop_event_trigger.rs new file mode 100644 index 0000000..46b1a05 --- /dev/null +++ b/tests/test_drop_event_trigger.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_event_trigger_parses() { + run_cases(&[ + r#"DROP EVENT TRIGGER trg"#, + r#"DROP EVENT TRIGGER IF EXISTS trg CASCADE"#, + ]); +} diff --git a/tests/test_drop_extension.rs b/tests/test_drop_extension.rs new file mode 100644 index 0000000..5ccf5e4 --- /dev/null +++ b/tests/test_drop_extension.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_extension_parses() { + run_cases(&[ + r#"DROP EXTENSION hstore"#, + r#"DROP EXTENSION IF EXISTS hstore, hstore_old CASCADE"#, + r#"DROP EXTENSION IF EXISTS hstore RESTRICT"#, + ]); +} diff --git a/tests/test_drop_foreign_data_wrapper.rs b/tests/test_drop_foreign_data_wrapper.rs new file mode 100644 index 0000000..0ac1c26 --- /dev/null +++ b/tests/test_drop_foreign_data_wrapper.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_foreign_data_wrapper_parses() { + run_cases(&[ + r#"DROP FOREIGN DATA WRAPPER my_fdw"#, + r#"DROP FOREIGN DATA WRAPPER IF EXISTS my_fdw, my_fdw_old CASCADE"#, + r#"DROP FOREIGN DATA WRAPPER IF EXISTS my_fdw RESTRICT"#, + ]); +} diff --git a/tests/test_drop_foreign_table.rs b/tests/test_drop_foreign_table.rs new file mode 100644 index 0000000..c8547cb --- /dev/null +++ b/tests/test_drop_foreign_table.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_foreign_table_parses() { + run_cases(&[ + r#"DROP FOREIGN TABLE ft"#, + r#"DROP FOREIGN TABLE IF EXISTS ft, ft_old CASCADE"#, + r#"DROP FOREIGN TABLE IF EXISTS ft RESTRICT"#, + ]); +} diff --git a/tests/test_drop_function.rs b/tests/test_drop_function.rs new file mode 100644 index 0000000..52f755a --- /dev/null +++ b/tests/test_drop_function.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_function_parses() { + run_cases(&[ + r#"DROP FUNCTION my_function(integer)"#, + r#"DROP FUNCTION IF EXISTS my_function(integer), my_function(text) CASCADE"#, + ]); +} diff --git a/tests/test_drop_group.rs b/tests/test_drop_group.rs new file mode 100644 index 0000000..699ac15 --- /dev/null +++ b/tests/test_drop_group.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_group_parses() { + run_cases(&[ + r#"DROP GROUP my_group"#, + r#"DROP GROUP IF EXISTS my_group, my_group_old"#, + ]); +} diff --git a/tests/test_drop_index.rs b/tests/test_drop_index.rs new file mode 100644 index 0000000..463f9a4 --- /dev/null +++ b/tests/test_drop_index.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_index_parses() { + run_cases(&[ + r#"DROP INDEX my_index"#, + r#"DROP INDEX IF EXISTS my_index, my_index_old CASCADE"#, + r#"DROP INDEX IF EXISTS my_index RESTRICT"#, + ]); +} diff --git a/tests/test_drop_language.rs b/tests/test_drop_language.rs new file mode 100644 index 0000000..a637e02 --- /dev/null +++ b/tests/test_drop_language.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_language_parses() { + run_cases(&[ + r#"DROP LANGUAGE plsample"#, + r#"DROP LANGUAGE IF EXISTS plsample, plsample_old CASCADE"#, + r#"DROP LANGUAGE IF EXISTS plsample RESTRICT"#, + ]); +} diff --git a/tests/test_drop_materialized_view.rs b/tests/test_drop_materialized_view.rs new file mode 100644 index 0000000..48f8f4f --- /dev/null +++ b/tests/test_drop_materialized_view.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_materialized_view_parses() { + run_cases(&[ + r#"DROP MATERIALIZED VIEW mv"#, + r#"DROP MATERIALIZED VIEW IF EXISTS mv, mv_old CASCADE"#, + r#"DROP MATERIALIZED VIEW IF EXISTS mv RESTRICT"#, + ]); +} diff --git a/tests/test_drop_operator.rs b/tests/test_drop_operator.rs new file mode 100644 index 0000000..95d4c5a --- /dev/null +++ b/tests/test_drop_operator.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_operator_parses() { + run_cases(&[ + r#"DROP OPERATOR +(integer, integer)"#, + r#"DROP OPERATOR IF EXISTS +(integer, integer), -(integer, integer) CASCADE"#, + ]); +} diff --git a/tests/test_drop_operator_class.rs b/tests/test_drop_operator_class.rs new file mode 100644 index 0000000..ace6972 --- /dev/null +++ b/tests/test_drop_operator_class.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_operator_class_parses() { + run_cases(&[ + r#"DROP OPERATOR CLASS my_opclass USING btree"#, + r#"DROP OPERATOR CLASS IF EXISTS my_opclass USING btree CASCADE"#, + r#"DROP OPERATOR CLASS IF EXISTS my_opclass USING btree RESTRICT"#, + ]); +} diff --git a/tests/test_drop_operator_family.rs b/tests/test_drop_operator_family.rs new file mode 100644 index 0000000..56cb3cc --- /dev/null +++ b/tests/test_drop_operator_family.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_operator_family_parses() { + run_cases(&[ + r#"DROP OPERATOR FAMILY my_family USING btree"#, + r#"DROP OPERATOR FAMILY IF EXISTS my_family USING btree CASCADE"#, + r#"DROP OPERATOR FAMILY IF EXISTS my_family USING btree RESTRICT"#, + ]); +} diff --git a/tests/test_drop_owned.rs b/tests/test_drop_owned.rs new file mode 100644 index 0000000..fe76d08 --- /dev/null +++ b/tests/test_drop_owned.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_owned_parses() { + run_cases(&[ + r#"DROP OWNED BY app_user"#, + r#"DROP OWNED BY app_user, other_user CASCADE"#, + ]); +} diff --git a/tests/test_drop_policy.rs b/tests/test_drop_policy.rs new file mode 100644 index 0000000..c4d22c0 --- /dev/null +++ b/tests/test_drop_policy.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_policy_parses() { + run_cases(&[ + r#"DROP POLICY pol ON my_table"#, + r#"DROP POLICY IF EXISTS pol ON my_table"#, + ]); +} diff --git a/tests/test_drop_procedure.rs b/tests/test_drop_procedure.rs new file mode 100644 index 0000000..544654f --- /dev/null +++ b/tests/test_drop_procedure.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_procedure_parses() { + run_cases(&[ + r#"DROP PROCEDURE my_proc(integer)"#, + r#"DROP PROCEDURE IF EXISTS my_proc(integer), my_proc(text) CASCADE"#, + ]); +} diff --git a/tests/test_drop_publication.rs b/tests/test_drop_publication.rs new file mode 100644 index 0000000..899156c --- /dev/null +++ b/tests/test_drop_publication.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_publication_parses() { + run_cases(&[ + r#"DROP PUBLICATION pub"#, + r#"DROP PUBLICATION IF EXISTS pub, pub_old CASCADE"#, + r#"DROP PUBLICATION IF EXISTS pub RESTRICT"#, + ]); +} diff --git a/tests/test_drop_role.rs b/tests/test_drop_role.rs new file mode 100644 index 0000000..94c7d24 --- /dev/null +++ b/tests/test_drop_role.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_role_parses() { + run_cases(&[ + r#"DROP ROLE app_user"#, + r#"DROP ROLE IF EXISTS app_user, app_user_old"#, + ]); +} diff --git a/tests/test_drop_routine.rs b/tests/test_drop_routine.rs new file mode 100644 index 0000000..d58d685 --- /dev/null +++ b/tests/test_drop_routine.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_routine_parses() { + run_cases(&[ + r#"DROP ROUTINE my_function(integer)"#, + r#"DROP ROUTINE IF EXISTS my_function(integer), my_proc(text) CASCADE"#, + ]); +} diff --git a/tests/test_drop_rule.rs b/tests/test_drop_rule.rs new file mode 100644 index 0000000..5f227e5 --- /dev/null +++ b/tests/test_drop_rule.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_rule_parses() { + run_cases(&[ + r#"DROP RULE my_rule ON my_table"#, + r#"DROP RULE IF EXISTS my_rule ON my_table CASCADE"#, + r#"DROP RULE IF EXISTS my_rule ON my_table RESTRICT"#, + ]); +} diff --git a/tests/test_drop_schema.rs b/tests/test_drop_schema.rs new file mode 100644 index 0000000..73c895f --- /dev/null +++ b/tests/test_drop_schema.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_schema_parses() { + run_cases(&[ + r#"DROP SCHEMA my_schema"#, + r#"DROP SCHEMA IF EXISTS my_schema, my_schema_old CASCADE"#, + r#"DROP SCHEMA IF EXISTS my_schema RESTRICT"#, + ]); +} diff --git a/tests/test_drop_sequence.rs b/tests/test_drop_sequence.rs new file mode 100644 index 0000000..d8b1081 --- /dev/null +++ b/tests/test_drop_sequence.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_sequence_parses() { + run_cases(&[ + r#"DROP SEQUENCE seq"#, + r#"DROP SEQUENCE IF EXISTS seq, seq_old CASCADE"#, + r#"DROP SEQUENCE IF EXISTS seq RESTRICT"#, + ]); +} diff --git a/tests/test_drop_server.rs b/tests/test_drop_server.rs new file mode 100644 index 0000000..31fd820 --- /dev/null +++ b/tests/test_drop_server.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_server_parses() { + run_cases(&[ + r#"DROP SERVER my_server"#, + r#"DROP SERVER IF EXISTS my_server, my_server_old CASCADE"#, + r#"DROP SERVER IF EXISTS my_server RESTRICT"#, + ]); +} diff --git a/tests/test_drop_statistics.rs b/tests/test_drop_statistics.rs new file mode 100644 index 0000000..ee0080c --- /dev/null +++ b/tests/test_drop_statistics.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_statistics_parses() { + run_cases(&[ + r#"DROP STATISTICS stats"#, + r#"DROP STATISTICS IF EXISTS stats, stats_old CASCADE"#, + r#"DROP STATISTICS IF EXISTS stats RESTRICT"#, + ]); +} diff --git a/tests/test_drop_subscription.rs b/tests/test_drop_subscription.rs new file mode 100644 index 0000000..bdaab96 --- /dev/null +++ b/tests/test_drop_subscription.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_subscription_parses() { + run_cases(&[ + r#"DROP SUBSCRIPTION sub"#, + r#"DROP SUBSCRIPTION IF EXISTS sub CASCADE"#, + r#"DROP SUBSCRIPTION IF EXISTS sub RESTRICT"#, + ]); +} diff --git a/tests/test_drop_table.rs b/tests/test_drop_table.rs new file mode 100644 index 0000000..eeec558 --- /dev/null +++ b/tests/test_drop_table.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_table_parses() { + run_cases(&[ + r#"DROP TABLE my_table"#, + r#"DROP TABLE IF EXISTS my_table, my_table_old CASCADE"#, + r#"DROP TABLE IF EXISTS my_table RESTRICT"#, + ]); +} diff --git a/tests/test_drop_tablespace.rs b/tests/test_drop_tablespace.rs new file mode 100644 index 0000000..45bc86a --- /dev/null +++ b/tests/test_drop_tablespace.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_tablespace_parses() { + run_cases(&[ + r#"DROP TABLESPACE fastspace"#, + r#"DROP TABLESPACE IF EXISTS fastspace"#, + ]); +} diff --git a/tests/test_drop_text_search_configuration.rs b/tests/test_drop_text_search_configuration.rs new file mode 100644 index 0000000..417fd11 --- /dev/null +++ b/tests/test_drop_text_search_configuration.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_text_search_configuration_parses() { + run_cases(&[ + r#"DROP TEXT SEARCH CONFIGURATION my_config"#, + r#"DROP TEXT SEARCH CONFIGURATION IF EXISTS my_config, my_config_old CASCADE"#, + r#"DROP TEXT SEARCH CONFIGURATION IF EXISTS my_config RESTRICT"#, + ]); +} diff --git a/tests/test_drop_text_search_dictionary.rs b/tests/test_drop_text_search_dictionary.rs new file mode 100644 index 0000000..c5a4972 --- /dev/null +++ b/tests/test_drop_text_search_dictionary.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_text_search_dictionary_parses() { + run_cases(&[ + r#"DROP TEXT SEARCH DICTIONARY my_dict"#, + r#"DROP TEXT SEARCH DICTIONARY IF EXISTS my_dict, my_dict_old CASCADE"#, + r#"DROP TEXT SEARCH DICTIONARY IF EXISTS my_dict RESTRICT"#, + ]); +} diff --git a/tests/test_drop_text_search_parser.rs b/tests/test_drop_text_search_parser.rs new file mode 100644 index 0000000..d13ab73 --- /dev/null +++ b/tests/test_drop_text_search_parser.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_text_search_parser_parses() { + run_cases(&[ + r#"DROP TEXT SEARCH PARSER my_parser"#, + r#"DROP TEXT SEARCH PARSER IF EXISTS my_parser, my_parser_old CASCADE"#, + r#"DROP TEXT SEARCH PARSER IF EXISTS my_parser RESTRICT"#, + ]); +} diff --git a/tests/test_drop_text_search_template.rs b/tests/test_drop_text_search_template.rs new file mode 100644 index 0000000..3098b71 --- /dev/null +++ b/tests/test_drop_text_search_template.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_text_search_template_parses() { + run_cases(&[ + r#"DROP TEXT SEARCH TEMPLATE my_template"#, + r#"DROP TEXT SEARCH TEMPLATE IF EXISTS my_template, my_template_old CASCADE"#, + r#"DROP TEXT SEARCH TEMPLATE IF EXISTS my_template RESTRICT"#, + ]); +} diff --git a/tests/test_drop_transform.rs b/tests/test_drop_transform.rs new file mode 100644 index 0000000..e33bdd0 --- /dev/null +++ b/tests/test_drop_transform.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_transform_parses() { + run_cases(&[ + r#"DROP TRANSFORM FOR my_type LANGUAGE plsample"#, + r#"DROP TRANSFORM IF EXISTS FOR my_type LANGUAGE plsample CASCADE"#, + ]); +} diff --git a/tests/test_drop_trigger.rs b/tests/test_drop_trigger.rs new file mode 100644 index 0000000..220e828 --- /dev/null +++ b/tests/test_drop_trigger.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_trigger_parses() { + run_cases(&[ + r#"DROP TRIGGER trg ON my_table"#, + r#"DROP TRIGGER IF EXISTS trg ON my_table CASCADE"#, + ]); +} diff --git a/tests/test_drop_type.rs b/tests/test_drop_type.rs new file mode 100644 index 0000000..914df29 --- /dev/null +++ b/tests/test_drop_type.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_type_parses() { + run_cases(&[ + r#"DROP TYPE my_type"#, + r#"DROP TYPE IF EXISTS my_type, my_type_old CASCADE"#, + r#"DROP TYPE IF EXISTS my_type RESTRICT"#, + ]); +} diff --git a/tests/test_drop_user.rs b/tests/test_drop_user.rs new file mode 100644 index 0000000..1e00754 --- /dev/null +++ b/tests/test_drop_user.rs @@ -0,0 +1,11 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_user_parses() { + run_cases(&[ + r#"DROP USER app_user"#, + r#"DROP USER IF EXISTS app_user, app_user_old"#, + ]); +} diff --git a/tests/test_drop_user_mapping.rs b/tests/test_drop_user_mapping.rs new file mode 100644 index 0000000..3610cd0 --- /dev/null +++ b/tests/test_drop_user_mapping.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_user_mapping_parses() { + run_cases(&[ + r#"DROP USER MAPPING FOR app_user SERVER my_server"#, + r#"DROP USER MAPPING IF EXISTS FOR CURRENT_USER SERVER my_server"#, + r#"DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER my_server"#, + ]); +} diff --git a/tests/test_drop_view.rs b/tests/test_drop_view.rs new file mode 100644 index 0000000..808ca4f --- /dev/null +++ b/tests/test_drop_view.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn drop_view_parses() { + run_cases(&[ + r#"DROP VIEW v"#, + r#"DROP VIEW IF EXISTS v, v_old CASCADE"#, + r#"DROP VIEW IF EXISTS v RESTRICT"#, + ]); +} diff --git a/tests/test_import_foreign_schema.rs b/tests/test_import_foreign_schema.rs new file mode 100644 index 0000000..98839e2 --- /dev/null +++ b/tests/test_import_foreign_schema.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn import_foreign_schema_parses() { + run_cases(&[ + r#"IMPORT FOREIGN SCHEMA public FROM SERVER my_server INTO local_schema"#, + r#"IMPORT FOREIGN SCHEMA public LIMIT TO (t1, t2) FROM SERVER my_server INTO local_schema OPTIONS (import_default 'true')"#, + r#"IMPORT FOREIGN SCHEMA public EXCEPT (t1, t2) FROM SERVER my_server INTO local_schema"#, + ]); +} diff --git a/tests/test_insert_grammar.rs b/tests/test_insert_grammar.rs new file mode 100644 index 0000000..3a28f21 --- /dev/null +++ b/tests/test_insert_grammar.rs @@ -0,0 +1,84 @@ +mod common; + +use common::run_parse_walk_cases as run_cases; + +#[test] +fn insert_target_variations() { + run_cases(&[ + "INSERT INTO users DEFAULT VALUES", + "INSERT INTO public.users DEFAULT VALUES", + "INSERT INTO users AS u DEFAULT VALUES", + "INSERT INTO users (id, name) VALUES (1, 'Sage')", + "INSERT INTO users AS u (id, name) VALUES (1, 'Sage')", + ]); +} + +#[test] +fn insert_with_clause_variations() { + run_cases(&[ + "WITH incoming AS (SELECT 1 AS id, 'Sage' AS name) INSERT INTO users SELECT * FROM incoming", + "WITH RECURSIVE incoming(id) AS (SELECT 1 UNION ALL SELECT id + 1 FROM incoming WHERE id < 3) INSERT INTO users (id) SELECT id FROM incoming", + "WITH incoming AS MATERIALIZED (SELECT 1 AS id) INSERT INTO users (id) SELECT id FROM incoming", + "WITH incoming AS NOT MATERIALIZED (SELECT 1 AS id) INSERT INTO users (id) SELECT id FROM incoming", + ]); +} + +#[test] +fn insert_value_source_variations() { + run_cases(&[ + "INSERT INTO users DEFAULT VALUES", + "INSERT INTO users (id, name) VALUES (1, 'Sage')", + "INSERT INTO users (id, name) VALUES (1, DEFAULT)", + "INSERT INTO users (id, name) VALUES (1, 'Sage'), (2, 'River')", + "INSERT INTO users (id, name) VALUES ($1, $2)", + "INSERT INTO users (id, name) SELECT id, name FROM incoming", + "INSERT INTO users SELECT * FROM incoming", + ]); +} + +#[test] +fn insert_overriding_variations() { + run_cases(&[ + "INSERT INTO users (id) OVERRIDING SYSTEM VALUE VALUES (1)", + "INSERT INTO users (id) OVERRIDING USER VALUE VALUES (1)", + "INSERT INTO users (id) OVERRIDING SYSTEM VALUE SELECT id FROM incoming", + "INSERT INTO users (id) OVERRIDING USER VALUE SELECT id FROM incoming", + ]); +} + +#[test] +fn insert_on_conflict_target_variations() { + run_cases(&[ + "INSERT INTO users (id) VALUES (1) ON CONFLICT DO NOTHING", + "INSERT INTO users (id) VALUES (1) ON CONFLICT (id) DO NOTHING", + "INSERT INTO users (email) VALUES ('a@example.com') ON CONFLICT ((lower(email))) DO NOTHING", + "INSERT INTO users (email) VALUES ('a@example.com') ON CONFLICT (email COLLATE \"C\") DO NOTHING", + "INSERT INTO users (email) VALUES ('a@example.com') ON CONFLICT (email text_ops) DO NOTHING", + "INSERT INTO users (email) VALUES ('a@example.com') ON CONFLICT (email) WHERE active DO NOTHING", + "INSERT INTO users (email) VALUES ('a@example.com') ON CONFLICT ON CONSTRAINT users_email_key DO NOTHING", + ]); +} + +#[test] +fn insert_on_conflict_action_variations() { + run_cases(&[ + "INSERT INTO users (id, name) VALUES (1, 'Sage') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name", + "INSERT INTO users (id, name) VALUES (1, 'Sage') ON CONFLICT (id) DO UPDATE SET name = DEFAULT", + "INSERT INTO users (id, name, active) VALUES (1, 'Sage', true) ON CONFLICT (id) DO UPDATE SET (name, active) = ROW (EXCLUDED.name, DEFAULT)", + "INSERT INTO users (id, name, active) VALUES (1, 'Sage', true) ON CONFLICT (id) DO UPDATE SET (name, active) = (EXCLUDED.name, DEFAULT)", + "INSERT INTO users (id, name, active) VALUES (1, 'Sage', true) ON CONFLICT (id) DO UPDATE SET (name, active) = (SELECT EXCLUDED.name, EXCLUDED.active)", + "INSERT INTO users (id, name) VALUES (1, 'Sage') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name WHERE users.name IS DISTINCT FROM EXCLUDED.name", + ]); +} + +#[test] +fn insert_returning_variations() { + run_cases(&[ + "INSERT INTO users DEFAULT VALUES RETURNING *", + "INSERT INTO users (id) VALUES (1) RETURNING id", + "INSERT INTO users (id) VALUES (1) RETURNING id AS inserted_id, email new_email", + "INSERT INTO users (id) VALUES (1) RETURNING WITH (OLD AS old_row) id", + "INSERT INTO users (id) VALUES (1) RETURNING WITH (NEW AS new_row) new_row.id", + "INSERT INTO users (id) VALUES (1) ON CONFLICT (id) DO UPDATE SET id = EXCLUDED.id RETURNING WITH (OLD AS old_row, NEW AS new_row) old_row.id AS old_id, new_row.id AS new_id", + ]); +} diff --git a/tests/test_refresh_materialized_view.rs b/tests/test_refresh_materialized_view.rs new file mode 100644 index 0000000..08a9981 --- /dev/null +++ b/tests/test_refresh_materialized_view.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn refresh_materialized_view_parses() { + run_cases(&[ + r#"REFRESH MATERIALIZED VIEW mv"#, + r#"REFRESH MATERIALIZED VIEW CONCURRENTLY mv WITH NO DATA"#, + r#"REFRESH MATERIALIZED VIEW mv WITH DATA"#, + ]); +} diff --git a/tests/test_reindex.rs b/tests/test_reindex.rs new file mode 100644 index 0000000..4d53433 --- /dev/null +++ b/tests/test_reindex.rs @@ -0,0 +1,15 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn reindex_parses() { + run_cases(&[ + r#"REINDEX INDEX my_index"#, + r#"REINDEX TABLE CONCURRENTLY my_table"#, + r#"REINDEX SCHEMA public"#, + r#"REINDEX DATABASE mydb"#, + r#"REINDEX SYSTEM mydb"#, + r#"REINDEX (VERBOSE true, TABLESPACE fastspace) INDEX CONCURRENTLY my_index"#, + ]); +} diff --git a/tests/test_security_label.rs b/tests/test_security_label.rs new file mode 100644 index 0000000..80216e3 --- /dev/null +++ b/tests/test_security_label.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn security_label_parses() { + run_cases(&[ + r#"SECURITY LABEL ON TABLE my_table IS 'classified'"#, + r#"SECURITY LABEL FOR selinux ON COLUMN my_table.id IS 'system_u:object_r:sepgsql_table_t:s0'"#, + r#"SECURITY LABEL ON FUNCTION my_function(integer) IS NULL"#, + ]); +} diff --git a/tests/test_select_grammar.rs b/tests/test_select_grammar.rs new file mode 100644 index 0000000..5c1f3f7 --- /dev/null +++ b/tests/test_select_grammar.rs @@ -0,0 +1,182 @@ +mod common; + +use common::run_parse_walk_cases as run_cases; + +#[test] +fn select_clause_variations() { + run_cases(&[ + "SELECT", + "SELECT ALL", + "SELECT ALL 1", + "SELECT DISTINCT id FROM users", + "SELECT DISTINCT ON (account_id, created_at) account_id, created_at FROM events", + "SELECT *", + "SELECT id AS user_id, name display_name, 1 + 2", + "SELECT FROM users", + "SELECT id FROM users WHERE active AND age >= 18", + "SELECT department, count(*) FROM employees GROUP BY department HAVING count(*) > 1", + "SELECT sum(salary) OVER w FROM employees WINDOW w AS (PARTITION BY department ORDER BY hired_at)", + "SELECT sum(salary) OVER w, avg(salary) OVER v FROM employees WINDOW w AS (PARTITION BY department), v AS (ORDER BY hired_at)", + "SELECT id FROM users ORDER BY created_at ASC NULLS LAST, id DESC NULLS FIRST", + "SELECT id FROM users ORDER BY created_at NULLS FIRST", + "SELECT id FROM users ORDER BY name USING < NULLS FIRST", + "SELECT id FROM users LIMIT 10", + "SELECT id FROM users LIMIT ALL", + "SELECT id FROM users OFFSET 5", + "SELECT id FROM users OFFSET 5 ROW", + "SELECT id FROM users OFFSET 5 ROWS", + "SELECT id FROM users FETCH FIRST ROW ONLY", + "SELECT id FROM users FETCH FIRST 10 ROWS ONLY", + "SELECT id FROM users FETCH NEXT 10 ROW ONLY", + "SELECT id FROM users FETCH NEXT ROWS ONLY", + "SELECT id FROM users ORDER BY id FETCH NEXT 10 ROWS WITH TIES", + "SELECT id FROM users ORDER BY id FETCH NEXT ROW WITH TIES", + "SELECT id FROM users WHERE active GROUP BY id HAVING count(*) > 0 WINDOW w AS (ORDER BY id) ORDER BY id LIMIT 5 OFFSET 1 ROWS", + ]); +} + +#[test] +fn with_clause_variations() { + run_cases(&[ + "WITH w AS (SELECT 1 AS id) SELECT * FROM w", + "WITH w(id, name) AS (SELECT 1, 'a') SELECT id, name FROM w", + "WITH w AS MATERIALIZED (SELECT 1 AS id) SELECT * FROM w", + "WITH w AS NOT MATERIALIZED (SELECT 1 AS id) SELECT * FROM w", + "WITH w AS (VALUES (1), (2)) SELECT * FROM w", + "WITH w AS (INSERT INTO users (id) VALUES (1) RETURNING id) SELECT * FROM w", + "WITH w AS (UPDATE users SET active = true RETURNING id) SELECT * FROM w", + "WITH w AS (DELETE FROM users WHERE active = false RETURNING id) SELECT * FROM w", + "WITH w AS (MERGE INTO users u USING incoming i ON u.id = i.id WHEN MATCHED THEN UPDATE SET active = true RETURNING u.id) SELECT * FROM w", + "WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 3) SELECT * FROM t", + "WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 3) SEARCH BREADTH FIRST BY n SET ordercol SELECT * FROM t", + "WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 3) SEARCH DEPTH FIRST BY n SET ordercol SELECT * FROM t", + "WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 3) CYCLE n SET is_cycle USING path SELECT * FROM t", + "WITH RECURSIVE t(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 3) CYCLE n SET is_cycle TO true DEFAULT false USING path SELECT * FROM t", + "WITH RECURSIVE t(n, m) AS (SELECT 1, 2 UNION ALL SELECT n + 1, m + 1 FROM t WHERE n < 3) SEARCH BREADTH FIRST BY n, m SET ordercol SELECT * FROM t", + "WITH RECURSIVE t(n, m) AS (SELECT 1, 2 UNION ALL SELECT n + 1, m + 1 FROM t WHERE n < 3) CYCLE n, m SET is_cycle USING path SELECT * FROM t", + "WITH one AS (SELECT 1), two AS (SELECT 2) SELECT * FROM one, two", + ]); +} + +#[test] +fn from_item_variations() { + run_cases(&[ + "SELECT * FROM users", + "SELECT * FROM ONLY users", + "SELECT * FROM users *", + "SELECT * FROM users AS u", + "SELECT * FROM users u", + "SELECT * FROM users AS u (id, name)", + "SELECT * FROM users TABLESAMPLE bernoulli (10)", + "SELECT * FROM users AS u TABLESAMPLE system (25) REPEATABLE (42)", + "SELECT * FROM (SELECT 1 AS id) s", + "SELECT * FROM LATERAL (SELECT 1 AS id) AS s(id)", + "SELECT * FROM LATERAL (SELECT 1 AS id) s", + "WITH w AS (SELECT 1 AS id) SELECT * FROM w AS cte(id)", + "SELECT * FROM foo()", + "SELECT * FROM generate_series(1, 3)", + "SELECT * FROM generate_series(1, 3) WITH ORDINALITY", + "SELECT * FROM generate_series(1, 3) g(value)", + "SELECT * FROM LATERAL generate_series(1, 3) WITH ORDINALITY AS g(value, ordinality)", + "SELECT * FROM json_to_record('{\"a\":1}') AS x(a int)", + "SELECT * FROM json_to_record('{\"a\":1}') x(a int)", + "SELECT * FROM LATERAL json_to_record('{\"a\":1}') AS (a int)", + "SELECT * FROM ROWS FROM (generate_series(1, 3), generate_series(4, 6))", + "SELECT * FROM ROWS FROM (json_to_record('{\"a\":1}') AS (a int))", + "SELECT * FROM LATERAL ROWS FROM (generate_series(1, 3)) AS r(n)", + "SELECT * FROM ROWS FROM (json_to_record('{\"a\":1}') AS (a int), generate_series(1, 3)) WITH ORDINALITY AS r(a, b, ordinality)", + "SELECT * FROM users JOIN orders ON users.id = orders.user_id", + "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id", + "SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id", + "SELECT * FROM users LEFT OUTER JOIN orders ON users.id = orders.user_id", + "SELECT * FROM users RIGHT JOIN orders USING (user_id)", + "SELECT * FROM users RIGHT OUTER JOIN orders USING (user_id)", + "SELECT * FROM users FULL OUTER JOIN orders USING (user_id)", + "SELECT * FROM users FULL JOIN orders USING (user_id) AS joined_cols", + "SELECT * FROM users JOIN orders USING (user_id, tenant_id)", + "SELECT * FROM users NATURAL INNER JOIN orders", + "SELECT * FROM users NATURAL LEFT JOIN orders", + "SELECT * FROM users NATURAL FULL OUTER JOIN orders", + "SELECT * FROM users CROSS JOIN orders", + "SELECT * FROM users, orders, LATERAL generate_series(1, users.id) AS g(n)", + ]); +} + +#[test] +fn grouping_element_variations() { + run_cases(&[ + "SELECT count(*) FROM users GROUP BY ()", + "SELECT department, count(*) FROM employees GROUP BY department", + "SELECT department, role, count(*) FROM employees GROUP BY (department, role)", + "SELECT department, role, count(*) FROM employees GROUP BY ALL department, role", + "SELECT department, role, count(*) FROM employees GROUP BY DISTINCT department, role", + "SELECT department, role, count(*) FROM employees GROUP BY ROLLUP (department, (role, location))", + "SELECT department, role, count(*) FROM employees GROUP BY CUBE (department, (role, location))", + "SELECT department, role, count(*) FROM employees GROUP BY GROUPING SETS ((), department, (department, role), ROLLUP (location, role), CUBE (department, location))", + ]); +} + +#[test] +fn set_operation_variations() { + run_cases(&[ + "SELECT 1 UNION SELECT 2", + "SELECT 1 UNION ALL SELECT 2", + "SELECT 1 UNION DISTINCT SELECT 2", + "SELECT 1 INTERSECT SELECT 1", + "SELECT 1 INTERSECT ALL SELECT 1", + "SELECT 1 INTERSECT DISTINCT SELECT 1", + "SELECT 1 EXCEPT SELECT 2", + "SELECT 1 EXCEPT ALL SELECT 2", + "SELECT 1 EXCEPT DISTINCT SELECT 2", + "(SELECT 1 UNION SELECT 2) EXCEPT SELECT 3 ORDER BY 1 LIMIT 1", + ]); +} + +#[test] +fn locking_clause_variations() { + run_cases(&[ + "SELECT * FROM users FOR UPDATE", + "SELECT * FROM users FOR NO KEY UPDATE", + "SELECT * FROM users FOR SHARE", + "SELECT * FROM users FOR KEY SHARE", + "SELECT * FROM users FOR UPDATE OF users", + "SELECT * FROM users u JOIN orders o ON u.id = o.user_id FOR UPDATE OF u, o", + "SELECT * FROM users FOR UPDATE NOWAIT", + "SELECT * FROM users FOR UPDATE SKIP LOCKED", + "SELECT * FROM users FOR UPDATE NOWAIT FOR SHARE SKIP LOCKED", + ]); +} + +#[test] +fn table_statement_variations() { + run_cases(&["TABLE users", "TABLE ONLY users", "TABLE users *"]); +} + +#[test] +fn parameter_variations() { + run_cases(&[ + "SELECT $1", + "SELECT $1::int AS id, coalesce($2, 'fallback') name", + "SELECT DISTINCT ON ($1) $1, id FROM users", + "SELECT * FROM users WHERE id = $1 AND active = $2", + "SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > $1)", + "SELECT department, count(*) FROM employees GROUP BY department HAVING count(*) > $1", + "SELECT count(*) FROM employees GROUP BY ROLLUP (department, $1)", + "SELECT sum(salary) OVER w FROM employees WINDOW w AS (PARTITION BY $1 ORDER BY hired_at)", + "SELECT * FROM users TABLESAMPLE bernoulli ($1) REPEATABLE ($2)", + "SELECT * FROM generate_series($1, $2)", + "SELECT * FROM ROWS FROM (generate_series($1, $2), generate_series($3, $4))", + "SELECT * FROM users JOIN orders ON users.id = orders.user_id AND orders.total > $1", + "WITH w AS (SELECT $1 AS id) SELECT * FROM w WHERE id = $2", + "WITH w AS (VALUES ($1), ($2)) SELECT * FROM w", + "WITH w AS (INSERT INTO users (id) VALUES ($1) RETURNING id) SELECT * FROM w", + "SELECT $1 UNION ALL SELECT $2", + "SELECT $1 INTERSECT SELECT $2", + "SELECT $1 EXCEPT SELECT $2", + "SELECT id FROM users ORDER BY coalesce($1, id) ASC NULLS LAST", + "SELECT id FROM users LIMIT $1", + "SELECT id FROM users OFFSET $1 ROWS", + "SELECT id FROM users FETCH FIRST $1 ROWS ONLY", + "SELECT id FROM users ORDER BY id FETCH NEXT $1 ROWS WITH TIES", + ]); +} diff --git a/tests/test_select_into.rs b/tests/test_select_into.rs new file mode 100644 index 0000000..2c8ebf6 --- /dev/null +++ b/tests/test_select_into.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn select_into_parses() { + run_cases(&[ + r#"SELECT 1 AS id INTO new_table"#, + r#"SELECT 1 AS id INTO TEMP TABLE new_table"#, + r#"SELECT 1 AS id INTO UNLOGGED TABLE new_table FROM source_table WHERE id > 0"#, + ]); +} diff --git a/tests/test_truncate.rs b/tests/test_truncate.rs new file mode 100644 index 0000000..82c46fd --- /dev/null +++ b/tests/test_truncate.rs @@ -0,0 +1,12 @@ +mod common; + +use common::run_parse_debug_cases as run_cases; + +#[test] +fn truncate_parses() { + run_cases(&[ + r#"TRUNCATE my_table"#, + r#"TRUNCATE TABLE ONLY my_table, other_table RESTART IDENTITY CASCADE"#, + r#"TRUNCATE TABLE my_table CONTINUE IDENTITY RESTRICT"#, + ]); +} diff --git a/tests/test_update_grammar.rs b/tests/test_update_grammar.rs new file mode 100644 index 0000000..e2e77fd --- /dev/null +++ b/tests/test_update_grammar.rs @@ -0,0 +1,71 @@ +mod common; + +use common::run_parse_walk_cases as run_cases; + +#[test] +fn update_target_variations() { + run_cases(&[ + "UPDATE users SET name = 'Sage'", + "UPDATE ONLY users SET name = 'Sage'", + "UPDATE users * SET name = 'Sage'", + "UPDATE users AS u SET name = 'Sage'", + "UPDATE users u SET name = 'Sage'", + "UPDATE public.users AS u SET name = 'Sage'", + ]); +} + +#[test] +fn update_with_clause_variations() { + run_cases(&[ + "WITH incoming AS (SELECT 1 AS id, 'Sage' AS name) UPDATE users SET name = incoming.name FROM incoming WHERE users.id = incoming.id", + "WITH RECURSIVE incoming(id) AS (SELECT 1 UNION ALL SELECT id + 1 FROM incoming WHERE id < 3) UPDATE users SET active = true FROM incoming WHERE users.id = incoming.id", + "WITH incoming AS MATERIALIZED (SELECT 1 AS id) UPDATE users SET active = true FROM incoming WHERE users.id = incoming.id", + "WITH incoming AS NOT MATERIALIZED (SELECT 1 AS id) UPDATE users SET active = true FROM incoming WHERE users.id = incoming.id", + ]); +} + +#[test] +fn update_set_variations() { + run_cases(&[ + "UPDATE users SET name = 'Sage'", + "UPDATE users SET name = DEFAULT", + "UPDATE users SET name = $1, active = $2", + "UPDATE users SET (name, active) = ROW ('Sage', true)", + "UPDATE users SET (name, active) = ('Sage', DEFAULT)", + "UPDATE users SET (name, active) = (SELECT name, active FROM incoming WHERE incoming.id = users.id)", + "UPDATE users SET name = upper(name), updated_at = now()", + ]); +} + +#[test] +fn update_from_variations() { + run_cases(&[ + "UPDATE users SET active = false FROM orders WHERE users.id = orders.user_id", + "UPDATE users SET active = false FROM orders o, sessions s WHERE users.id = o.user_id AND users.id = s.user_id", + "UPDATE users SET active = false FROM LATERAL (SELECT 1 AS id) s WHERE users.id = s.id", + "UPDATE users SET active = false FROM generate_series(1, 3) AS g(id) WHERE users.id = g.id", + "UPDATE users SET active = false FROM orders JOIN invoices ON orders.id = invoices.order_id WHERE users.id = orders.user_id", + ]); +} + +#[test] +fn update_where_variations() { + run_cases(&[ + "UPDATE users SET active = false WHERE id = 1", + "UPDATE users SET active = $1 WHERE id = $2", + "UPDATE users SET active = false WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)", + "UPDATE users SET active = false WHERE CURRENT OF user_cursor", + ]); +} + +#[test] +fn update_returning_variations() { + run_cases(&[ + "UPDATE users SET active = false RETURNING *", + "UPDATE users SET active = false RETURNING id", + "UPDATE users SET active = false RETURNING id AS updated_id, email new_email", + "UPDATE users SET active = false RETURNING WITH (OLD AS old_row) old_row.active", + "UPDATE users SET active = true RETURNING WITH (NEW AS new_row) new_row.active", + "UPDATE users SET active = true RETURNING WITH (OLD AS old_row, NEW AS new_row) old_row.active AS was_active, new_row.active AS is_active", + ]); +}