squawk_parser/
parse.rs

1use crate::ast::RootStmt;
2use crate::error::{PGQueryError, ParseError};
3use libpg_query::{pg_query_free_parse_result, pg_query_parse};
4use serde::Deserialize;
5use serde_json::Value;
6use std::ffi::{CStr, CString};
7
8fn c_ptr_to_string(str_ptr: *mut ::std::os::raw::c_char) -> Option<String> {
9    if str_ptr.is_null() {
10        None
11    } else {
12        unsafe { CStr::from_ptr(str_ptr) }
13            .to_str()
14            .ok()
15            .map(|s| s.to_owned())
16    }
17}
18
19fn parse_sql_query_base<'a, T>(query: &'a str) -> Result<Vec<T>, PGQueryError>
20where
21    T: Deserialize<'a>,
22{
23    let c_str = CString::new(query)?;
24    let pg_parse_result = unsafe { pg_query_parse(c_str.as_ptr()) };
25
26    if !pg_parse_result.error.is_null() {
27        let err = unsafe { *pg_parse_result.error };
28        let parse_error = ParseError {
29            message: c_ptr_to_string(err.message),
30            funcname: c_ptr_to_string(err.funcname),
31            filename: c_ptr_to_string(err.filename),
32            lineno: err.lineno,
33            cursorpos: err.cursorpos,
34            context: c_ptr_to_string(err.context),
35        };
36        return Err(PGQueryError::PGParseError(parse_error));
37    }
38
39    // not sure if this is ever null, but might as well check
40    if pg_parse_result.parse_tree.is_null() {
41        return Err(PGQueryError::ParsingCString);
42    }
43
44    let parse_tree = unsafe { CStr::from_ptr(pg_parse_result.parse_tree) }.to_str()?;
45    let output =
46        serde_json::from_str(parse_tree).map_err(|e| PGQueryError::JsonParse(e.to_string()));
47
48    unsafe {
49        pg_query_free_parse_result(pg_parse_result);
50    };
51
52    output
53}
54
55pub fn parse_sql_query_json(query: &str) -> Result<Vec<Value>, PGQueryError> {
56    parse_sql_query_base(query)
57}
58
59pub fn parse_sql_query(query: &str) -> Result<Vec<RootStmt>, PGQueryError> {
60    parse_sql_query_base(query)
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    use insta::assert_debug_snapshot;
68
69    #[test]
70    fn test_span_with_indent() {
71        // NOTE: the span information for these starts at 0 even though the SQL
72        // is offset.
73        let sql = r#"   SELECT 1;"#;
74        let res = parse_sql_query(sql);
75        assert_debug_snapshot!(res);
76    }
77    #[test]
78    fn test_span_with_new_line_and_indent() {
79        let sql = r#"
80    SELECT 1;"#;
81        let res = parse_sql_query(sql);
82        assert_debug_snapshot!(res);
83    }
84
85    #[test]
86    fn test_adding_index_non_concurrently() {
87        let sql = r#"
88  -- instead of
89  CREATE INDEX "field_name_idx" ON "table_name" ("field_name");
90  -- use CONCURRENTLY
91  CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name");
92  "#;
93
94        let res = parse_sql_query(sql);
95
96        assert_debug_snapshot!(res);
97    }
98
99    #[test]
100    fn test_error_paths() {
101        let sql = r#"lsakdjf;asdlfkjasd;lfj"#;
102        let res = parse_sql_query(sql);
103        assert_debug_snapshot!(res);
104    }
105
106    #[test]
107    fn test_migration() {
108        let sql = r#"
109BEGIN;
110CREATE INDEX "table_name_field_name_idx" ON "table_name" ("field_name");
111CREATE INDEX "table_name_field_name_idx" ON "table_name" ("field_name" varchar_pattern_ops);
112COMMIT;
113"#;
114        let res = parse_sql_query(sql);
115        assert_debug_snapshot!(res);
116    }
117
118    #[test]
119    fn test_select_string_literal() {
120        let sql = r#"SELECT 'some string';"#;
121        let res = parse_sql_query(sql);
122        assert_debug_snapshot!(res);
123    }
124
125    #[test]
126    fn test_select_one() {
127        let sql = r#"SELECT 1;"#;
128        let res = parse_sql_query(sql);
129        assert_debug_snapshot!(res);
130    }
131    #[test]
132    fn test_parse_sql_create_index_concurrently() {
133        let sql = r#"CREATE INDEX CONCURRENTLY "table_name_idx" ON "table_name" ("table_field");"#;
134        let res = parse_sql_query(sql);
135        assert_debug_snapshot!(res);
136    }
137
138    #[test]
139    fn test_parsing_insert_stmt() {
140        let sql = r#"INSERT INTO table_name VALUES (1, 2, 3);"#;
141        let res = parse_sql_query(sql);
142        assert_debug_snapshot!(res)
143    }
144
145    #[test]
146    fn test_parsing_update_stmt() {
147        let sql = r#"UPDATE table_name SET foo = 'bar' WHERE buzz > 10;"#;
148        let res = parse_sql_query(sql);
149        assert_debug_snapshot!(res)
150    }
151
152    #[test]
153    fn test_parsing_create_table() {
154        let sql = r#"
155BEGIN;
156CREATE TABLE "core_foo" (
157  "id" serial NOT NULL PRIMARY KEY, 
158  "created" timestamp with time zone NOT NULL, 
159  "modified" timestamp with time zone NOT NULL, 
160  "mongo_id" varchar(255) NOT NULL UNIQUE, 
161  "description" text NOT NULL, 
162  "metadata" jsonb NOT NULL, 
163  "kind" varchar(255) NOT NULL, 
164  "age" integer NOT NULL, 
165  "tenant_id" integer NULL
166);
167CREATE INDEX "age_index" ON "core_foo" ("age");
168COMMIT;
169"#;
170        let res = parse_sql_query(sql);
171        assert_debug_snapshot!(res)
172    }
173
174    #[test]
175    fn test_parse_sql_create_index() {
176        let sql = r#"CREATE INDEX "table_name_idx" ON "table_name" ("table_field");"#;
177        let res = parse_sql_query(sql);
178        assert_debug_snapshot!(res);
179    }
180    #[test]
181    fn test_parse_sql_create_unique_index_safe() {
182        let sql = r#"
183ALTER TABLE "legacy_questiongrouppg" 
184    ADD CONSTRAINT "legacy_questiongrouppg_mongo_id_1f8f47d9_uniq" UNIQUE 
185    USING INDEX "legacy_questiongrouppg_mongo_id_1f8f47d9_uniq_idx";
186"#;
187        let res = parse_sql_query(sql);
188        assert_debug_snapshot!(res);
189    }
190    #[test]
191    fn test_parse_delete_stmt() {
192        let sql = r#"DELETE FROM "table_name";"#;
193        let res = parse_sql_query(sql);
194        assert_debug_snapshot!(res);
195
196        let sql = r#"DELETE FROM "table_name" WHERE account_age > 10;"#;
197        let res = parse_sql_query(sql);
198        assert_debug_snapshot!(res);
199    }
200
201    #[test]
202    fn test_parse_set_operations_stmt() {
203        let sql = r#"SELECT * from "table_name" UNION SELECT * from "table_foo";"#;
204        let res = parse_sql_query(sql);
205        assert_debug_snapshot!(res);
206
207        let sql = r#"SELECT * from "table_name" UNION ALL SELECT * from "table_foo";"#;
208        let res = parse_sql_query(sql);
209        assert_debug_snapshot!(res);
210    }
211
212    #[test]
213    fn test_parse_create_schema_stmt() {
214        let sql = r#"CREATE SCHEMA schema_name;"#;
215        let res = parse_sql_query(sql);
216        assert_debug_snapshot!(res);
217    }
218
219    #[test]
220    fn test_parse_replica_identity_stmt() {
221        let sql = "ALTER TABLE aa REPLICA IDENTITY FULL;";
222        let res = parse_sql_query(sql);
223        assert_debug_snapshot!(res);
224    }
225
226    #[test]
227    fn test_parse_alter_collation_stmt() {
228        let sql = "ALTER COLLATION name RENAME TO new_name;";
229        let res = parse_sql_query(sql);
230        assert_debug_snapshot!(res);
231    }
232
233    #[test]
234    fn test_parse_alter_domain_stmt() {
235        let sql = "ALTER DOMAIN zipcode SET NOT NULL;";
236        let res = parse_sql_query(sql);
237        assert_debug_snapshot!(res);
238    }
239
240    #[test]
241    fn test_parsing_grant_stmt() {
242        let sql = "GRANT INSERT ON films TO PUBLIC;";
243        let res = parse_sql_query(sql);
244        assert_debug_snapshot!(res);
245    }
246
247    #[test]
248    fn test_parsing_grant_role() {
249        let sql = "GRANT admins TO joe;";
250        let res = parse_sql_query(sql);
251        assert_debug_snapshot!(res);
252    }
253
254    #[test]
255    fn test_alter_default_privileges_stmt() {
256        let sql = "ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLES TO PUBLIC;";
257        let res = parse_sql_query(sql);
258        assert_debug_snapshot!(res);
259    }
260
261    #[test]
262    fn test_parsing_copy_stmt() {
263        let sql = "COPY country FROM '/usr1/proj/bray/sql/country_data';";
264        let res = parse_sql_query(sql);
265        assert_debug_snapshot!(res);
266    }
267
268    #[test]
269    fn test_parsing_variable_set_stmt() {
270        let sql = "set session my.vars.id = '1';";
271        let res = parse_sql_query(sql);
272        assert_debug_snapshot!(res);
273    }
274
275    #[test]
276    fn test_parsing_variable_show_stmt() {
277        let sql = "SHOW name";
278        let res = parse_sql_query(sql);
279        assert_debug_snapshot!(res);
280    }
281
282    #[test]
283    fn test_parsing_create_table_space_stmt() {
284        let sql = "CREATE TABLESPACE dbspace LOCATION '/data/dbs';";
285        let res = parse_sql_query(sql);
286        assert_debug_snapshot!(res);
287    }
288
289    #[test]
290    fn test_parsing_drop_table_space_stmt() {
291        let sql = "DROP TABLESPACE dbspace;";
292        let res = parse_sql_query(sql);
293        assert_debug_snapshot!(res);
294    }
295
296    #[test]
297    fn test_alter_table_space_stmt() {
298        let sql = "ALTER TABLESPACE index_space RENAME TO fast_raid;";
299        let res = parse_sql_query(sql);
300        assert_debug_snapshot!(res);
301    }
302
303    #[test]
304    fn test_create_extension() {
305        let sql = "CREATE EXTENSION hstore;";
306        let res = parse_sql_query(sql);
307        assert_debug_snapshot!(res);
308    }
309
310    #[test]
311    fn test_alter_table_extension() {
312        let sql = "ALTER EXTENSION hstore UPDATE TO '2.0';";
313        let res = parse_sql_query(sql);
314        assert_debug_snapshot!(res);
315    }
316
317    #[test]
318    fn test_drop_extension() {
319        let sql = "DROP EXTENSION hstore;";
320        let res = parse_sql_query(sql);
321        assert_debug_snapshot!(res);
322    }
323
324    #[test]
325    fn test_alter_extension_contents_stmt() {
326        let sql = "ALTER EXTENSION hstore SET SCHEMA utils;";
327        let res = parse_sql_query(sql);
328        assert_debug_snapshot!(res);
329
330        let sql = "ALTER EXTENSION hstore ADD FUNCTION populate_record(anyelement, hstore);";
331        let res = parse_sql_query(sql);
332        assert_debug_snapshot!(res);
333    }
334
335    #[test]
336    fn test_create_foreign_data_wrapper() {
337        let sql = "CREATE FOREIGN DATA WRAPPER dummy;";
338        let res = parse_sql_query(sql);
339        assert_debug_snapshot!(res);
340    }
341
342    #[test]
343    fn test_alter_foreign_data_wrapper() {
344        let sql = "ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar');";
345        let res = parse_sql_query(sql);
346        assert_debug_snapshot!(res);
347    }
348
349    #[test]
350    fn test_create_foreign_server_stmt() {
351        let sql = "CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432');";
352        let res = parse_sql_query(sql);
353        assert_debug_snapshot!(res);
354    }
355
356    #[test]
357    fn test_alter_foreign_server_stmt() {
358        let sql = "ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb');";
359        let res = parse_sql_query(sql);
360        assert_debug_snapshot!(res);
361    }
362
363    #[test]
364    fn test_create_foriegn_table_stmt() {
365        let sql = r#"
366CREATE FOREIGN TABLE films (
367    code        char(5) NOT NULL,
368    title       varchar(40) NOT NULL,
369    did         integer NOT NULL,
370    date_prod   date,
371    kind        varchar(10),
372    len         interval hour to minute
373)
374SERVER film_server;
375"#;
376        let res = parse_sql_query(sql);
377        assert_debug_snapshot!(res);
378    }
379
380    #[test]
381    fn test_create_user_mapping_stmt() {
382        let sql = "CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret');";
383        let res = parse_sql_query(sql);
384        assert_debug_snapshot!(res);
385    }
386
387    #[test]
388    fn test_alter_user_mapping_stmt() {
389        let sql = "ALTER USER MAPPING FOR bob SERVER foo OPTIONS (SET password 'public');";
390        let res = parse_sql_query(sql);
391        assert_debug_snapshot!(res);
392    }
393
394    #[test]
395    fn test_drop_user_mapping_stmt() {
396        let sql = "DROP USER MAPPING IF EXISTS FOR bob SERVER foo;";
397        let res = parse_sql_query(sql);
398        assert_debug_snapshot!(res);
399    }
400
401    #[test]
402    fn test_import_foreign_schema_stmt() {
403        let sql = r#"
404IMPORT FOREIGN SCHEMA foreign_films
405    FROM SERVER film_server INTO films;
406"#;
407        let res = parse_sql_query(sql);
408        assert_debug_snapshot!(res);
409    }
410
411    #[test]
412    fn test_create_policy_stmt() {
413        let sql = "CREATE POLICY name ON table_name FOR ALL;";
414        let res = parse_sql_query(sql);
415        assert_debug_snapshot!(res);
416    }
417
418    #[test]
419    fn test_alter_policy_stmt() {
420        let sql = "ALTER POLICY name ON table_name RENAME TO new_name;";
421        let res = parse_sql_query(sql);
422        assert_debug_snapshot!(res);
423
424        let sql = "ALTER POLICY name ON table_name TO PUBLIC WITH CHECK (account_age > 10);";
425        let res = parse_sql_query(sql);
426        assert_debug_snapshot!(res);
427    }
428
429    #[test]
430    fn test_create_access_method_stmt() {
431        let sql = "CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;";
432        let res = parse_sql_query(sql);
433        assert_debug_snapshot!(res);
434    }
435
436    #[test]
437    fn test_create_trigger_stmt() {
438        let sql = r#"
439CREATE TRIGGER check_update
440    BEFORE UPDATE ON accounts
441    FOR EACH ROW
442    EXECUTE PROCEDURE check_account_update();
443"#;
444        let res = parse_sql_query(sql);
445        assert_debug_snapshot!(res);
446    }
447
448    #[test]
449    fn test_create_event_trigger_stmt() {
450        let sql = r#"
451CREATE EVENT TRIGGER abort_ddl ON ddl_command_start
452   EXECUTE PROCEDURE abort_any_command();
453"#;
454        let res = parse_sql_query(sql);
455        assert_debug_snapshot!(res);
456    }
457
458    #[test]
459    fn test_alter_event_trigger_stmt() {
460        let sql = r#"
461ALTER EVENT TRIGGER name DISABLE;
462"#;
463        let res = parse_sql_query(sql);
464        assert_debug_snapshot!(res);
465    }
466
467    /// Postgres >=11 feature not supported in libpg_query
468    #[test]
469    fn test_create_procedure_stmt() {
470        let sql = r#"
471CREATE PROCEDURE insert_data(a integer, b integer)
472LANGUAGE SQL
473AS $$
474INSERT INTO tbl VALUES (a);
475INSERT INTO tbl VALUES (b);
476$$;
477
478CALL insert_data(1, 2);
479"#;
480        let res = parse_sql_query(sql);
481        assert_debug_snapshot!(res);
482    }
483
484    #[test]
485    fn test_create_function_stmt() {
486        let sql = r#"
487CREATE FUNCTION populate() RETURNS integer AS $$
488DECLARE
489    -- declarations
490BEGIN
491    PERFORM my_function();
492END;
493$$ LANGUAGE plpgsql;
494"#;
495        let res = parse_sql_query(sql);
496        assert_debug_snapshot!(res);
497    }
498
499    #[test]
500    fn test_create_plang_stmt() {
501        let sql = r#"
502CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql
503    HANDLER plpgsql_call_handler
504    VALIDATOR plpgsql_validator;
505"#;
506        let res = parse_sql_query(sql);
507        assert_debug_snapshot!(res);
508    }
509
510    #[test]
511    fn test_create_role_stmt() {
512        let sql = r#"
513CREATE ROLE miriam 
514    WITH LOGIN PASSWORD 'jw8s0F4' 
515    VALID UNTIL '2005-01-01';
516"#;
517        let res = parse_sql_query(sql);
518        assert_debug_snapshot!(res);
519    }
520
521    #[test]
522    fn test_alter_role_stmt() {
523        let sql = r#"
524ALTER ROLE miriam CREATEROLE CREATEDB;
525"#;
526        let res = parse_sql_query(sql);
527        assert_debug_snapshot!(res);
528    }
529
530    #[test]
531    fn test_alter_role_set_stmt() {
532        let sql = r#"
533ALTER ROLE worker_bee SET maintenance_work_mem = 100000;
534"#;
535        let res = parse_sql_query(sql);
536        assert_debug_snapshot!(res);
537    }
538
539    #[test]
540    fn test_drop_role_set_stmt() {
541        let sql = r#"
542DROP ROLE jonathan;
543"#;
544        let res = parse_sql_query(sql);
545        assert_debug_snapshot!(res);
546    }
547
548    #[test]
549    fn test_create_sequence_stmt() {
550        let sql = r#"
551CREATE SEQUENCE serial START 101;
552"#;
553        let res = parse_sql_query(sql);
554        assert_debug_snapshot!(res);
555    }
556
557    #[test]
558    fn test_alter_sequence_stmt() {
559        let sql = r#"
560ALTER SEQUENCE serial RESTART WITH 105;
561"#;
562        let res = parse_sql_query(sql);
563        assert_debug_snapshot!(res);
564    }
565
566    #[test]
567    fn test_define_stmt() {
568        let sql = r#"
569CREATE AGGREGATE sum (complex)
570(
571    sfunc = complex_add,
572    stype = complex,
573    initcond = '(0,0)'
574);
575"#;
576        let res = parse_sql_query(sql);
577        assert_debug_snapshot!(res);
578
579        let sql = r#"
580CREATE OPERATOR === (
581    LEFTARG = box,
582    RIGHTARG = box,
583    PROCEDURE = area_equal_procedure,
584    COMMUTATOR = ===,
585    NEGATOR = !==,
586    RESTRICT = area_restriction_procedure,
587    JOIN = area_join_procedure,
588    HASHES, MERGES
589);
590"#;
591        let res = parse_sql_query(sql);
592        assert_debug_snapshot!(res);
593
594        let sql = r#"
595CREATE TYPE box (
596    INTERNALLENGTH = 16,
597    INPUT = my_box_in_function,
598    OUTPUT = my_box_out_function,
599    ELEMENT = float4
600);
601"#;
602        let res = parse_sql_query(sql);
603        assert_debug_snapshot!(res);
604    }
605
606    #[test]
607    fn test_create_domain_stmt() {
608        let sql = r#"
609CREATE DOMAIN us_postal_code AS TEXT
610CHECK(
611   VALUE ~ '^\d{5}$'
612OR VALUE ~ '^\d{5}-\d{4}$'
613);
614"#;
615        let res = parse_sql_query(sql);
616        assert_debug_snapshot!(res);
617    }
618
619    #[test]
620    fn test_create_op_class_stmt() {
621        let sql = r#"
622CREATE OPERATOR CLASS gist__int_ops
623    DEFAULT FOR TYPE _int4 USING gist AS
624        OPERATOR        3       &&,
625        OPERATOR        6       = (anyarray, anyarray),
626        OPERATOR        7       @>,
627        OPERATOR        8       <@,
628        OPERATOR        20      @@ (_int4, query_int),
629        FUNCTION        1       g_int_consistent (internal, _int4, int, oid, internal),
630        FUNCTION        2       g_int_union (internal, internal),
631        FUNCTION        3       g_int_compress (internal),
632        FUNCTION        4       g_int_decompress (internal),
633        FUNCTION        5       g_int_penalty (internal, internal, internal),
634        FUNCTION        6       g_int_picksplit (internal, internal),
635        FUNCTION        7       g_int_same (_int4, _int4, internal);
636"#;
637        let res = parse_sql_query(sql);
638        assert_debug_snapshot!(res);
639    }
640
641    #[test]
642    fn test_alter_op_class_stmt() {
643        let sql = r#"
644ALTER OPERATOR CLASS name USING index_method RENAME TO new_name;
645ALTER OPERATOR CLASS name USING index_method
646    OWNER TO CURRENT_USER;
647ALTER OPERATOR CLASS name USING index_method
648    SET SCHEMA new_schema;
649"#;
650        let res = parse_sql_query(sql);
651        assert_debug_snapshot!(res);
652    }
653
654    #[test]
655    fn test_alter_op_family_stmt() {
656        let sql = r#"
657ALTER OPERATOR FAMILY integer_ops USING btree ADD
658
659  -- int4 vs int2
660  OPERATOR 1 < (int4, int2) ,
661  OPERATOR 2 <= (int4, int2) ,
662  OPERATOR 3 = (int4, int2) ,
663  OPERATOR 4 >= (int4, int2) ,
664  OPERATOR 5 > (int4, int2) ,
665  FUNCTION 1 btint42cmp(int4, int2) ,
666
667  -- int2 vs int4
668  OPERATOR 1 < (int2, int4) ,
669  OPERATOR 2 <= (int2, int4) ,
670  OPERATOR 3 = (int2, int4) ,
671  OPERATOR 4 >= (int2, int4) ,
672  OPERATOR 5 > (int2, int4) ,
673  FUNCTION 1 btint24cmp(int2, int4) ;
674"#;
675        let res = parse_sql_query(sql);
676        assert_debug_snapshot!(res);
677    }
678
679    #[test]
680    fn test_truncate_stmt() {
681        let sql = r#"
682TRUNCATE bigtable, fattable, bar RESTART IDENTITY;
683TRUNCATE foo CASCADE;
684"#;
685        let res = parse_sql_query(sql);
686        assert_debug_snapshot!(res);
687    }
688
689    #[test]
690    fn test_comment_on_stmt() {
691        let sql = r#"
692COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
693"#;
694        let res = parse_sql_query(sql);
695        assert_debug_snapshot!(res);
696    }
697
698    #[test]
699    fn test_security_label_stmt() {
700        let sql = r#"
701SECURITY LABEL FOR selinux ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
702"#;
703        let res = parse_sql_query(sql);
704        assert_debug_snapshot!(res);
705    }
706
707    #[test]
708    fn test_declare_cursor_stmt() {
709        let sql = r#"
710DECLARE
711    curs2 CURSOR FOR SELECT * FROM tenk1;
712"#;
713        let res = parse_sql_query(sql);
714        assert_debug_snapshot!(res);
715    }
716
717    #[test]
718    fn test_close_portal_stmt() {
719        let sql = r#"
720CLOSE curs1;
721"#;
722        let res = parse_sql_query(sql);
723        assert_debug_snapshot!(res);
724    }
725
726    #[test]
727    fn test_fetch_stmt() {
728        let sql = r#"
729FETCH FORWARD 5 FROM foo;
730"#;
731        let res = parse_sql_query(sql);
732        assert_debug_snapshot!(res);
733    }
734
735    #[test]
736    fn test_create_stats_stmt() {
737        let sql = r#"
738CREATE STATISTICS s1 (dependencies) ON a, b FROM t1;
739"#;
740        let res = parse_sql_query(sql);
741        assert_debug_snapshot!(res);
742    }
743
744    #[test]
745    fn test_explain_stmt() {
746        let sql = r#"
747EXPLAIN ANALYZE SELECT * FROM t1 WHERE (a = 1) AND (b = 0);
748"#;
749        let res = parse_sql_query(sql);
750        assert_debug_snapshot!(res);
751    }
752
753    #[test]
754    fn test_alter_function_stmt() {
755        let sql = r#"
756ALTER FUNCTION sqrt(integer) RENAME TO square_root;
757ALTER FUNCTION sqrt(integer) OWNER TO joe;
758ALTER FUNCTION sqrt(integer) SET SCHEMA maths;
759ALTER FUNCTION check_password(text) SET search_path = admin, pg_temp;
760ALTER FUNCTION check_password(text) RESET search_path;
761"#;
762        let res = parse_sql_query(sql);
763        assert_debug_snapshot!(res);
764    }
765
766    #[test]
767    fn test_do_stmt() {
768        let sql = r#"
769DO $$DECLARE r record;
770BEGIN
771    FOR r IN SELECT table_schema, table_name FROM information_schema.tables
772             WHERE table_type = 'VIEW' AND table_schema = 'public'
773    LOOP
774        EXECUTE 'GRANT ALL ON ' || quote_ident(r.table_schema) || '.' || quote_ident(r.table_name) || ' TO webuser';
775    END LOOP;
776END$$;
777"#;
778        let res = parse_sql_query(sql);
779        assert_debug_snapshot!(res);
780    }
781
782    #[test]
783    fn test_alter_object_depends_stmt() {
784        let sql = r#"
785ALTER TRIGGER name ON table_name 
786    DEPENDS ON EXTENSION extension_name;
787ALTER FUNCTION sqrt(integer) 
788    DEPENDS ON EXTENSION extension_name;
789"#;
790        let res = parse_sql_query(sql);
791        assert_debug_snapshot!(res);
792    }
793
794    #[test]
795    fn test_alter_operator_stmt() {
796        let sql = r#"
797ALTER OPERATOR @@ (text, text) OWNER TO joe;
798ALTER OPERATOR @@ (text, text) SET SCHEMA bar;
799ALTER OPERATOR && (_int4, _int4) SET (RESTRICT = _int_contsel, JOIN = _int_contjoinsel);
800"#;
801        let res = parse_sql_query(sql);
802        assert_debug_snapshot!(res);
803    }
804
805    #[test]
806    fn test_rule_stmt() {
807        let sql = r#"
808CREATE RULE "_RETURN" AS
809    ON SELECT TO t1
810    DO INSTEAD
811        SELECT * FROM t2;
812
813CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;
814"#;
815        let res = parse_sql_query(sql);
816        assert_debug_snapshot!(res);
817    }
818
819    #[test]
820    fn test_notify_stmt() {
821        let sql = r#"
822NOTIFY virtual;
823NOTIFY virtual, 'This is the payload';
824"#;
825        let res = parse_sql_query(sql);
826        assert_debug_snapshot!(res);
827    }
828
829    #[test]
830    fn test_listen_stmt() {
831        let sql = r#"
832LISTEN virtual;
833"#;
834        let res = parse_sql_query(sql);
835        assert_debug_snapshot!(res);
836    }
837
838    #[test]
839    fn test_unlisten_stmt() {
840        let sql = r#"
841UNLISTEN virtual;
842"#;
843        let res = parse_sql_query(sql);
844        assert_debug_snapshot!(res);
845    }
846
847    #[test]
848    fn test_composite_type_stmt() {
849        let sql = r#"
850CREATE TYPE complex AS (
851    r       double precision,
852    i       double precision
853);
854"#;
855        let res = parse_sql_query(sql);
856        assert_debug_snapshot!(res);
857    }
858
859    #[test]
860    fn test_create_enum_stmt() {
861        let sql = r#"
862CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
863"#;
864        let res = parse_sql_query(sql);
865        assert_debug_snapshot!(res);
866    }
867
868    #[test]
869    fn test_create_range_stmt() {
870        let sql = r#"
871CREATE TYPE floatrange AS RANGE (
872    subtype = float8,
873    subtype_diff = float8mi
874);
875"#;
876        let res = parse_sql_query(sql);
877        assert_debug_snapshot!(res);
878    }
879
880    #[test]
881    fn test_alter_enum_stmt() {
882        let sql = r#"
883ALTER TYPE colors ADD VALUE 'orange' AFTER 'red';
884"#;
885        let res = parse_sql_query(sql);
886        assert_debug_snapshot!(res);
887    }
888
889    #[test]
890    fn test_create_view_stmt() {
891        let sql = r#"
892CREATE VIEW vista AS SELECT 'Hello World';
893CREATE VIEW comedies AS
894    SELECT *
895    FROM films
896    WHERE kind = 'Comedy';
897"#;
898        let res = parse_sql_query(sql);
899        assert_debug_snapshot!(res);
900    }
901
902    #[test]
903    fn test_load_stmt() {
904        let sql = r#"
905LOAD 'filename';
906"#;
907        let res = parse_sql_query(sql);
908        assert_debug_snapshot!(res);
909    }
910
911    #[test]
912    fn test_create_database_stmt() {
913        let sql = r#"
914CREATE DATABASE lusiadas;
915CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace;
916CREATE DATABASE music ENCODING 'LATIN1' TEMPLATE template0;
917"#;
918        let res = parse_sql_query(sql);
919        assert_debug_snapshot!(res);
920    }
921
922    #[test]
923    fn test_alter_database_stmt() {
924        let sql = r#"
925ALTER DATABASE name RENAME TO new_name;
926ALTER DATABASE name OWNER TO new_owner;
927ALTER DATABASE name SET TABLESPACE new_tablespace;
928ALTER DATABASE name RESET configuration_parameter;
929ALTER DATABASE name RESET ALL;
930"#;
931        let res = parse_sql_query(sql);
932        assert_debug_snapshot!(res);
933    }
934
935    #[test]
936    fn test_drop_database_stmt() {
937        let sql = r#"
938DROP DATABASE name;
939DROP DATABASE IF EXISTS name;
940"#;
941        let res = parse_sql_query(sql);
942        assert_debug_snapshot!(res);
943    }
944
945    #[test]
946    fn test_alter_system_stmt() {
947        let sql = r#"
948ALTER SYSTEM SET wal_level = hot_standby;
949ALTER SYSTEM RESET wal_level;
950"#;
951        let res = parse_sql_query(sql);
952        assert_debug_snapshot!(res);
953    }
954
955    #[test]
956    fn test_cluster_stmt() {
957        let sql = r#"
958CLUSTER employees USING employees_ind;
959CLUSTER employees;
960CLUSTER;
961"#;
962        let res = parse_sql_query(sql);
963        assert_debug_snapshot!(res);
964    }
965
966    #[test]
967    fn test_vacuum_stmt() {
968        let sql = r#"
969VACUUM (VERBOSE, ANALYZE) foo;
970"#;
971        let res = parse_sql_query(sql);
972        assert_debug_snapshot!(res);
973    }
974
975    #[test]
976    fn test_create_table_as_stmt() {
977        let sql = r#"
978CREATE TABLE films2 AS
979  TABLE films;
980"#;
981        let res = parse_sql_query(sql);
982        assert_debug_snapshot!(res);
983    }
984
985    #[test]
986    fn test_refresh_material_view_stmt() {
987        let sql = r#"
988REFRESH MATERIALIZED VIEW order_summary;
989REFRESH MATERIALIZED VIEW annual_statistics_basis WITH NO DATA;
990"#;
991        let res = parse_sql_query(sql);
992        assert_debug_snapshot!(res);
993    }
994
995    #[test]
996    fn test_checkpoint() {
997        let sql = r#"
998CHECKPOINT;
999"#;
1000        let res = parse_sql_query(sql);
1001        assert_debug_snapshot!(res);
1002    }
1003
1004    #[test]
1005    fn test_discard_stmt() {
1006        let sql = r#"
1007DISCARD PLANS;
1008DISCARD SEQUENCES;
1009DISCARD TEMP;
1010"#;
1011        let res = parse_sql_query(sql);
1012        assert_debug_snapshot!(res);
1013    }
1014
1015    #[test]
1016    fn test_lock_stmt() {
1017        let sql = r#"
1018LOCK TABLE films IN SHARE MODE;
1019LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
1020"#;
1021        let res = parse_sql_query(sql);
1022        assert_debug_snapshot!(res);
1023    }
1024
1025    #[test]
1026    fn test_set_constraints() {
1027        let sql = r#"
1028SET CONSTRAINTS ALL DEFERRED;
1029SET CONSTRAINTS ALL IMMEDIATE;
1030SET CONSTRAINTS foo IMMEDIATE;
1031"#;
1032        let res = parse_sql_query(sql);
1033        assert_debug_snapshot!(res);
1034    }
1035
1036    #[test]
1037    fn test_reindex_stmt() {
1038        let sql = r#"
1039REINDEX INDEX my_index;
1040REINDEX TABLE table_name;
1041REINDEX DATABASE table_name;
1042REINDEX SYSTEM table_name;
1043"#;
1044        let res = parse_sql_query(sql);
1045        assert_debug_snapshot!(res);
1046    }
1047
1048    #[test]
1049    fn test_create_conversion_stmt() {
1050        let sql = r#"
1051CREATE CONVERSION myconv FOR 'UTF8' TO 'LATIN1' FROM myfunc;
1052"#;
1053        let res = parse_sql_query(sql);
1054        assert_debug_snapshot!(res);
1055    }
1056
1057    #[test]
1058    fn test_create_cast_stmt() {
1059        let sql = r#"
1060CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT;
1061"#;
1062        let res = parse_sql_query(sql);
1063        assert_debug_snapshot!(res);
1064    }
1065
1066    #[test]
1067    fn test_create_transform_stmt() {
1068        let sql = r#"
1069CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
1070    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
1071    TO SQL WITH FUNCTION plpython_to_hstore(internal)
1072);
1073"#;
1074        let res = parse_sql_query(sql);
1075        assert_debug_snapshot!(res);
1076    }
1077
1078    #[test]
1079    fn test_prepare_stmt() {
1080        let sql = r#"
1081PREPARE fooplan (int, text, bool, numeric) AS
1082    INSERT INTO foo VALUES($1, $2, $3, $4);
1083"#;
1084        let res = parse_sql_query(sql);
1085        assert_debug_snapshot!(res);
1086    }
1087
1088    #[test]
1089    fn test_execute_stmt() {
1090        let sql = r#"
1091EXECUTE fooplan(1, 'Hunter Valley', 't', 200.00);
1092"#;
1093        let res = parse_sql_query(sql);
1094        assert_debug_snapshot!(res);
1095    }
1096
1097    #[test]
1098    fn test_deallocate_stmt() {
1099        let sql = r#"
1100DEALLOCATE PREPARE ALL;
1101"#;
1102        let res = parse_sql_query(sql);
1103        assert_debug_snapshot!(res);
1104    }
1105
1106    #[test]
1107    fn test_drop_owned_stmt() {
1108        let sql = r#"
1109DROP OWNED BY foo CASCADE;
1110"#;
1111        let res = parse_sql_query(sql);
1112        assert_debug_snapshot!(res);
1113    }
1114
1115    #[test]
1116    fn test_reassign_owned_stmt() {
1117        let sql = r#"
1118REASSIGN OWNED BY old_role TO new_role;
1119"#;
1120        let res = parse_sql_query(sql);
1121        assert_debug_snapshot!(res);
1122    }
1123
1124    #[test]
1125    fn test_alter_ts_dictionary_stmt() {
1126        let sql = r#"
1127ALTER TEXT SEARCH DICTIONARY my_dict ( StopWords = newrussian );
1128"#;
1129        let res = parse_sql_query(sql);
1130        assert_debug_snapshot!(res);
1131    }
1132
1133    #[test]
1134    fn test_alter_ts_configuration_stmt() {
1135        let sql = r#"
1136ALTER TEXT SEARCH CONFIGURATION astro_en
1137    ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;
1138"#;
1139        let res = parse_sql_query(sql);
1140        assert_debug_snapshot!(res);
1141    }
1142
1143    #[test]
1144    fn test_create_publication_stmt() {
1145        let sql = r#"
1146CREATE PUBLICATION mypublication FOR TABLE users, departments;
1147CREATE PUBLICATION insert_only FOR TABLE mydata
1148    WITH (publish = 'insert');
1149"#;
1150        let res = parse_sql_query(sql);
1151        assert_debug_snapshot!(res);
1152    }
1153
1154    #[test]
1155    fn test_alter_publication() {
1156        let sql = r#"
1157ALTER PUBLICATION noinsert SET (publish = 'update, delete');
1158ALTER PUBLICATION mypublication ADD TABLE users, departments;
1159ALTER PUBLICATION name RENAME TO new_name
1160"#;
1161        let res = parse_sql_query(sql);
1162        assert_debug_snapshot!(res);
1163    }
1164
1165    #[test]
1166    fn test_create_subscription_stmt() {
1167        let sql = r#"
1168CREATE SUBSCRIPTION mysub
1169         CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
1170        PUBLICATION mypublication, insert_only;
1171"#;
1172        let res = parse_sql_query(sql);
1173        assert_debug_snapshot!(res);
1174    }
1175
1176    #[test]
1177    fn test_alter_subscription_stmt() {
1178        let sql = r#"
1179ALTER SUBSCRIPTION mysub SET PUBLICATION insert_only;
1180ALTER SUBSCRIPTION mysub DISABLE;
1181"#;
1182        let res = parse_sql_query(sql);
1183        assert_debug_snapshot!(res);
1184    }
1185
1186    #[test]
1187    fn test_drop_subscription_stmt() {
1188        let sql = r#"
1189DROP SUBSCRIPTION mysub;
1190"#;
1191        let res = parse_sql_query(sql);
1192        assert_debug_snapshot!(res);
1193    }
1194}