sqlformat/
lib.rs

1//! This crate is a port of https://github.com/kufii/sql-formatter-plus
2//! written in Rust. It is intended to be usable as a pure-Rust library
3//! for formatting SQL queries.
4
5#![type_length_limit = "99999999"]
6#![forbid(unsafe_code)]
7// Maintains semver compatibility for older Rust versions
8#![allow(clippy::manual_strip)]
9// This lint is overly pedantic and annoying
10#![allow(clippy::needless_lifetimes)]
11
12mod formatter;
13mod indentation;
14mod inline_block;
15mod params;
16mod tokenizer;
17
18/// Formats whitespace in a SQL string to make it easier to read.
19/// Optionally replaces parameter placeholders with `params`.
20pub fn format(query: &str, params: &QueryParams, options: &FormatOptions) -> String {
21    let named_placeholders = matches!(params, QueryParams::Named(_));
22
23    let tokens = tokenizer::tokenize(query, named_placeholders, options);
24    formatter::format(&tokens, params, options)
25}
26
27/// Options for controlling how the library formats SQL
28#[derive(Debug, Clone)]
29pub struct FormatOptions<'a> {
30    /// Controls the type and length of indentation to use
31    ///
32    /// Default: 2 spaces
33    pub indent: Indent,
34    /// When set, changes reserved keywords to ALL CAPS
35    ///
36    /// Default: false
37    pub uppercase: Option<bool>,
38    /// Controls the number of line breaks after a query
39    ///
40    /// Default: 1
41    pub lines_between_queries: u8,
42    /// Ignore case conversion for specified strings in the array.
43    ///
44    /// Default: None
45    pub ignore_case_convert: Option<Vec<&'a str>>,
46    /// Keep the query in a single line
47    ///
48    /// Default: false
49    pub inline: bool,
50    /// Maximum length of an inline block
51    ///
52    /// Default: 50
53    pub max_inline_block: usize,
54    /// Maximum length of inline arguments
55    ///
56    /// If unset keep every argument in a separate line
57    ///
58    /// Default: None
59    pub max_inline_arguments: Option<usize>,
60    /// Inline the argument at the top level if they would fit a line of this length
61    ///
62    /// Default: None
63    pub max_inline_top_level: Option<usize>,
64    /// Consider any JOIN statement as a top level keyword instead of a reserved keyword
65    ///
66    /// Default: false,
67    pub joins_as_top_level: bool,
68}
69
70impl<'a> Default for FormatOptions<'a> {
71    fn default() -> Self {
72        FormatOptions {
73            indent: Indent::Spaces(2),
74            uppercase: None,
75            lines_between_queries: 1,
76            ignore_case_convert: None,
77            inline: false,
78            max_inline_block: 50,
79            max_inline_arguments: None,
80            max_inline_top_level: None,
81            joins_as_top_level: false,
82        }
83    }
84}
85
86#[derive(Debug, Clone, Copy)]
87pub enum Indent {
88    Spaces(u8),
89    Tabs,
90}
91
92#[derive(Debug, Clone, Default)]
93pub enum QueryParams {
94    Named(Vec<(String, String)>),
95    Indexed(Vec<String>),
96    #[default]
97    None,
98}
99
100#[derive(Default, Debug, Clone)]
101pub(crate) struct SpanInfo {
102    pub full_span: usize,
103    pub blocks: usize,
104    pub newline_before: bool,
105    pub newline_after: bool,
106    pub arguments: usize,
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use indoc::indoc;
113    use pretty_assertions::assert_eq;
114
115    #[test]
116    fn test_sqlite_blob_literal_fmt() {
117        let options = FormatOptions::default();
118
119        let input = "SELECT x'73716c69676874' AS BLOB_VAL;";
120        let expected = indoc!(
121            "
122            SELECT
123              x'73716c69676874' AS BLOB_VAL;"
124        );
125        assert_eq!(format(input, &QueryParams::None, &options), expected);
126
127        let input = "SELECT X'73716c69676874' AS BLOB_VAL;";
128        let expected = indoc!(
129            "
130            SELECT
131              X'73716c69676874' AS BLOB_VAL;"
132        );
133        assert_eq!(format(input, &QueryParams::None, &options), expected);
134    }
135
136    #[test]
137    fn it_uses_given_indent_config_for_indentation() {
138        let input = "SELECT count(*),Column1 FROM Table1;";
139        let options = FormatOptions {
140            indent: Indent::Spaces(4),
141            ..FormatOptions::default()
142        };
143        let expected = indoc!(
144            "
145            SELECT
146                count(*),
147                Column1
148            FROM
149                Table1;"
150        );
151
152        assert_eq!(format(input, &QueryParams::None, &options), expected);
153    }
154
155    #[test]
156    fn it_formats_simple_set_schema_queries() {
157        let input = "SET SCHEMA schema1; SET CURRENT SCHEMA schema2;";
158        let options = FormatOptions::default();
159        let expected = indoc!(
160            "
161            SET SCHEMA
162              schema1;
163            SET CURRENT SCHEMA
164              schema2;"
165        );
166
167        assert_eq!(format(input, &QueryParams::None, &options), expected);
168    }
169
170    #[test]
171    fn it_formats_simple_select_query() {
172        let input = "SELECT count(*),Column1 FROM Table1;";
173        let options = FormatOptions::default();
174        let expected = indoc!(
175            "
176            SELECT
177              count(*),
178              Column1
179            FROM
180              Table1;"
181        );
182
183        assert_eq!(format(input, &QueryParams::None, &options), expected);
184    }
185
186    #[test]
187    fn it_formats_complex_select() {
188        let input =
189            "SELECT DISTINCT name, ROUND(age/7) field1, 18 + 20 AS field2, 'some string' FROM foo;";
190        let options = FormatOptions::default();
191        let expected = indoc!(
192            "
193            SELECT DISTINCT
194              name,
195              ROUND(age / 7) field1,
196              18 + 20 AS field2,
197              'some string'
198            FROM
199              foo;"
200        );
201
202        assert_eq!(format(input, &QueryParams::None, &options), expected);
203    }
204
205    #[test]
206    fn it_formats_over_with_window() {
207        let input =
208            "SELECT id, val, at, SUM(val) OVER win AS cumulative FROM data WINDOW win AS (PARTITION BY id ORDER BY at);";
209        let options = FormatOptions::default();
210        let expected = indoc!(
211            "
212            SELECT
213              id,
214              val,
215              at,
216              SUM(val) OVER win AS cumulative
217            FROM
218              data
219            WINDOW
220              win AS (
221                PARTITION BY
222                  id
223                ORDER BY
224                  at
225              );"
226        );
227
228        assert_eq!(format(input, &QueryParams::None, &options), expected);
229    }
230
231    #[test]
232    fn it_formats_distinct_from() {
233        let input = "SELECT bar IS DISTINCT FROM 'baz', IS NOT DISTINCT FROM 'foo' FROM foo;";
234        let options = FormatOptions::default();
235        let expected = indoc!(
236            "
237            SELECT
238              bar IS DISTINCT FROM 'baz',
239              IS NOT DISTINCT FROM 'foo'
240            FROM
241              foo;"
242        );
243
244        assert_eq!(format(input, &QueryParams::None, &options), expected);
245    }
246
247    #[test]
248    fn keep_select_arguments_inline() {
249        let input = indoc! {
250            "
251            SELECT
252              a,
253              b,
254              c,
255              d,
256              e,
257              f,
258              g,
259              h
260            FROM foo;"
261        };
262        let options = FormatOptions {
263            max_inline_arguments: Some(50),
264            ..Default::default()
265        };
266        let expected = indoc! {
267            "
268            SELECT
269              a, b, c, d, e, f, g, h
270            FROM
271              foo;"
272        };
273        assert_eq!(format(input, &QueryParams::None, &options), expected);
274    }
275
276    #[test]
277    fn split_select_arguments_inline_top_level() {
278        let input = indoc! {
279            "
280            SELECT
281              a,
282              b,
283              c,
284              d,
285              e,
286              f,
287              g,
288              h
289            FROM foo;"
290        };
291        let options = FormatOptions {
292            max_inline_arguments: Some(50),
293            max_inline_top_level: Some(50),
294            ..Default::default()
295        };
296        let expected = indoc! {
297            "
298            SELECT a, b, c, d, e, f, g, h
299            FROM foo;"
300        };
301        assert_eq!(format(input, &QueryParams::None, &options), expected);
302    }
303
304    #[test]
305    fn inline_arguments_when_possible() {
306        let input = indoc! {
307            "
308            SELECT
309              a,
310              b,
311              c,
312              d,
313              e,
314              f,
315              g,
316              h
317            FROM foo;"
318        };
319        let options = FormatOptions {
320            max_inline_arguments: Some(50),
321            max_inline_top_level: Some(20),
322            ..Default::default()
323        };
324        let expected = indoc! {
325            "
326            SELECT
327              a, b, c, d, e, f, g, h
328            FROM foo;"
329        };
330        assert_eq!(format(input, &QueryParams::None, &options), expected);
331    }
332
333    #[test]
334    fn inline_single_block_argument() {
335        let input = "SELECT a, b, c FROM ( SELECT (e+f) AS a, (m+o) AS b FROM d) WHERE (a != b) OR (c IS NULL AND a == b)";
336        let options = FormatOptions {
337            max_inline_arguments: Some(10),
338            max_inline_top_level: Some(20),
339            ..Default::default()
340        };
341        let expected = indoc! {
342            "
343            SELECT a, b, c
344            FROM (
345              SELECT
346                (e + f) AS a,
347                (m + o) AS b
348              FROM d
349            )
350            WHERE
351              (a != b)
352              OR (
353                c IS NULL
354                AND a == b
355              )"
356        };
357        assert_eq!(format(input, &QueryParams::None, &options), expected);
358    }
359
360    #[test]
361    fn it_formats_select_with_complex_where() {
362        let input = indoc!(
363            "
364            SELECT * FROM foo WHERE Column1 = 'testing'
365            AND ( (Column2 = Column3 OR Column4 >= NOW()) );
366      "
367        );
368        let options = FormatOptions::default();
369        let expected = indoc!(
370            "
371            SELECT
372              *
373            FROM
374              foo
375            WHERE
376              Column1 = 'testing'
377              AND (
378                (
379                  Column2 = Column3
380                  OR Column4 >= NOW()
381                )
382              );"
383        );
384
385        assert_eq!(format(input, &QueryParams::None, &options), expected);
386    }
387
388    #[test]
389    fn it_formats_select_with_complex_where_inline() {
390        let input = indoc!(
391            "
392            SELECT * FROM foo WHERE Column1 = 'testing'
393            AND ( (Column2 = Column3 OR Column4 >= NOW()) );
394      "
395        );
396        let options = FormatOptions {
397            max_inline_arguments: Some(100),
398            ..Default::default()
399        };
400        let expected = indoc!(
401            "
402            SELECT
403              *
404            FROM
405              foo
406            WHERE
407              Column1 = 'testing' AND ((Column2 = Column3 OR Column4 >= NOW()));"
408        );
409
410        assert_eq!(format(input, &QueryParams::None, &options), expected);
411    }
412
413    #[test]
414    fn it_formats_select_with_top_level_reserved_words() {
415        let input = indoc!(
416            "
417            SELECT * FROM foo WHERE name = 'John' GROUP BY some_column
418            HAVING column > 10 ORDER BY other_column LIMIT 5;
419      "
420        );
421        let options = FormatOptions::default();
422        let expected = indoc!(
423            "
424            SELECT
425              *
426            FROM
427              foo
428            WHERE
429              name = 'John'
430            GROUP BY
431              some_column
432            HAVING
433              column > 10
434            ORDER BY
435              other_column
436            LIMIT
437              5;"
438        );
439
440        assert_eq!(format(input, &QueryParams::None, &options), expected);
441    }
442
443    #[test]
444    fn it_formats_limit_with_two_comma_separated_values_on_single_line() {
445        let input = "LIMIT 5, 10;";
446        let options = FormatOptions::default();
447        let expected = indoc!(
448            "
449            LIMIT
450              5, 10;"
451        );
452
453        assert_eq!(format(input, &QueryParams::None, &options), expected);
454    }
455
456    #[test]
457    fn it_formats_limit_of_single_value_followed_by_another_select_using_commas() {
458        let input = "LIMIT 5; SELECT foo, bar;";
459        let options = FormatOptions::default();
460        let expected = indoc!(
461            "
462            LIMIT
463              5;
464            SELECT
465              foo,
466              bar;"
467        );
468
469        assert_eq!(format(input, &QueryParams::None, &options), expected);
470    }
471
472    #[test]
473    fn it_formats_type_specifiers() {
474        let input = "SELECT id,  ARRAY [] :: UUID [] FROM UNNEST($1  ::  UUID   []) WHERE $1::UUID[] IS NOT NULL;";
475        let options = FormatOptions::default();
476        let expected = indoc!(
477            "
478            SELECT
479              id,
480              ARRAY[]::UUID[]
481            FROM
482              UNNEST($1::UUID[])
483            WHERE
484              $1::UUID[] IS NOT NULL;"
485        );
486
487        assert_eq!(format(input, &QueryParams::None, &options), expected);
488    }
489
490    #[test]
491    fn it_formats_limit_of_single_value_and_offset() {
492        let input = "LIMIT 5 OFFSET 8;";
493        let options = FormatOptions::default();
494        let expected = indoc!(
495            "
496            LIMIT
497              5 OFFSET 8;"
498        );
499
500        assert_eq!(format(input, &QueryParams::None, &options), expected);
501    }
502
503    #[test]
504    fn it_recognizes_limit_in_lowercase() {
505        let input = "limit 5, 10;";
506        let options = FormatOptions::default();
507        let expected = indoc!(
508            "
509            limit
510              5, 10;"
511        );
512
513        assert_eq!(format(input, &QueryParams::None, &options), expected);
514    }
515
516    #[test]
517    fn it_preserves_case_of_keywords() {
518        let input = "select distinct * frOM foo left join bar WHERe a > 1 and b = 3";
519        let options = FormatOptions::default();
520        let expected = indoc!(
521            "
522            select distinct
523              *
524            frOM
525              foo
526              left join bar
527            WHERe
528              a > 1
529              and b = 3"
530        );
531
532        assert_eq!(format(input, &QueryParams::None, &options), expected);
533    }
534
535    #[test]
536    fn it_formats_select_query_with_select_query_inside_it() {
537        let input = "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b";
538        let options = FormatOptions::default();
539        let expected = indoc!(
540            "
541            SELECT
542              *,
543              SUM(*) AS sum
544            FROM
545              (
546                SELECT
547                  *
548                FROM
549                  Posts
550                LIMIT
551                  30
552              )
553            WHERE
554              a > b"
555        );
556
557        assert_eq!(format(input, &QueryParams::None, &options), expected);
558    }
559
560    #[test]
561    fn it_formats_select_query_with_inner_join() {
562        let input = indoc!(
563            "
564            SELECT customer_id.from, COUNT(order_id) AS total FROM customers
565            INNER JOIN orders ON customers.customer_id = orders.customer_id;"
566        );
567        let options = FormatOptions::default();
568        let expected = indoc!(
569            "
570            SELECT
571              customer_id.from,
572              COUNT(order_id) AS total
573            FROM
574              customers
575              INNER JOIN orders ON customers.customer_id = orders.customer_id;"
576        );
577
578        assert_eq!(format(input, &QueryParams::None, &options), expected);
579    }
580
581    #[test]
582    fn it_formats_select_query_with_non_standard_join() {
583        let input = indoc!(
584            "
585            SELECT customer_id.from, COUNT(order_id) AS total FROM customers
586            INNER ANY JOIN orders ON customers.customer_id = orders.customer_id
587            LEFT
588            SEMI JOIN foo ON foo.id = customers.id
589            PASTE
590            JOIN bar
591            ;"
592        );
593        let options = FormatOptions::default();
594        let expected = indoc!(
595            "
596            SELECT
597              customer_id.from,
598              COUNT(order_id) AS total
599            FROM
600              customers
601              INNER ANY JOIN orders ON customers.customer_id = orders.customer_id
602              LEFT SEMI JOIN foo ON foo.id = customers.id
603              PASTE JOIN bar;"
604        );
605
606        assert_eq!(format(input, &QueryParams::None, &options), expected);
607    }
608
609    #[test]
610    fn it_formats_select_query_with_non_standard_join_as_toplevel() {
611        let input = indoc!(
612            "
613            SELECT customer_id.from, COUNT(order_id) AS total FROM customers
614            INNER ANY JOIN orders ON customers.customer_id = orders.customer_id
615            LEFT
616            SEMI JOIN foo ON foo.id = customers.id
617            PASTE
618            JOIN bar
619            ;"
620        );
621        let options = FormatOptions {
622            joins_as_top_level: true,
623            max_inline_top_level: Some(40),
624            max_inline_arguments: Some(40),
625            ..Default::default()
626        };
627        let expected = indoc!(
628            "
629            SELECT
630              customer_id.from,
631              COUNT(order_id) AS total
632            FROM customers
633            INNER ANY JOIN
634              orders ON customers.customer_id = orders.customer_id
635            LEFT SEMI JOIN foo ON foo.id = customers.id
636            PASTE JOIN bar;"
637        );
638
639        assert_eq!(format(input, &QueryParams::None, &options), expected);
640    }
641
642    #[test]
643    fn it_formats_select_query_with_different_comments() {
644        let input = indoc!(
645            "
646            SELECT
647            /*
648             * This is a block comment
649             */
650            * FROM
651            -- This is another comment
652            MyTable # One final comment
653            WHERE 1 = 2;"
654        );
655        let options = FormatOptions::default();
656        let expected = indoc!(
657            "
658            SELECT
659              /*
660               * This is a block comment
661               */
662              *
663            FROM
664              -- This is another comment
665              MyTable # One final comment
666            WHERE
667              1 = 2;"
668        );
669
670        assert_eq!(format(input, &QueryParams::None, &options), expected);
671    }
672
673    #[test]
674    fn it_maintains_block_comment_indentation() {
675        let input = indoc!(
676            "
677            SELECT
678              /*
679               * This is a block comment
680               */
681              *
682            FROM
683              MyTable
684            WHERE
685              1 = 2;"
686        );
687        let options = FormatOptions::default();
688
689        assert_eq!(format(input, &QueryParams::None, &options), input);
690    }
691
692    #[test]
693    fn it_formats_simple_insert_query() {
694        let input = "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');";
695        let options = FormatOptions::default();
696        let expected = indoc!(
697            "
698            INSERT INTO
699              Customers (ID, MoneyBalance, Address, City)
700            VALUES
701              (12, -123.4, 'Skagen 2111', 'Stv');"
702        );
703
704        assert_eq!(format(input, &QueryParams::None, &options), expected);
705    }
706
707    #[test]
708    fn it_formats_complex_insert_query() {
709        let input = "
710 INSERT INTO t(id, a, min, max) SELECT input.id, input.a, input.min, input.max FROM ( SELECT id, a, min, max FROM foo WHERE a IN ('a', 'b') ) AS input WHERE (SELECT true FROM condition) ON CONFLICT ON CONSTRAINT a_id_key DO UPDATE SET id = EXCLUDED.id, a = EXCLUDED.severity, min = EXCLUDED.min, max = EXCLUDED.max RETURNING *; ";
711        let max_line = 50;
712        let options = FormatOptions {
713            max_inline_block: max_line,
714            max_inline_arguments: Some(max_line),
715            max_inline_top_level: Some(max_line),
716            ..Default::default()
717        };
718
719        let expected = indoc!(
720            "
721            INSERT INTO t(id, a, min, max)
722            SELECT input.id, input.a, input.min, input.max
723            FROM (
724              SELECT id, a, min, max
725              FROM foo
726              WHERE a IN ('a', 'b')
727            ) AS input
728            WHERE (SELECT true FROM condition)
729            ON CONFLICT ON CONSTRAINT a_id_key DO UPDATE SET
730              id = EXCLUDED.id,
731              a = EXCLUDED.severity,
732              min = EXCLUDED.min,
733              max = EXCLUDED.max
734            RETURNING *;"
735        );
736
737        assert_eq!(format(input, &QueryParams::None, &options), expected);
738    }
739
740    #[test]
741    fn it_keeps_short_parenthesized_list_with_nested_parenthesis_on_single_line() {
742        let input = "SELECT (a + b * (c - NOW()));";
743        let options = FormatOptions::default();
744        let expected = indoc!(
745            "
746            SELECT
747              (a + b * (c - NOW()));"
748        );
749
750        assert_eq!(format(input, &QueryParams::None, &options), expected);
751    }
752
753    #[test]
754    fn it_breaks_long_parenthesized_lists_to_multiple_lines() {
755        let input = indoc!(
756            "
757            INSERT INTO some_table (id_product, id_shop, id_currency, id_country, id_registration) (
758            SELECT IF(dq.id_discounter_shopping = 2, dq.value, dq.value / 100),
759            IF (dq.id_discounter_shopping = 2, 'amount', 'percentage') FROM foo);"
760        );
761        let options = FormatOptions::default();
762        let expected = indoc!(
763            "
764            INSERT INTO
765              some_table (
766                id_product,
767                id_shop,
768                id_currency,
769                id_country,
770                id_registration
771              ) (
772                SELECT
773                  IF (
774                    dq.id_discounter_shopping = 2,
775                    dq.value,
776                    dq.value / 100
777                  ),
778                  IF (
779                    dq.id_discounter_shopping = 2,
780                    'amount',
781                    'percentage'
782                  )
783                FROM
784                  foo
785              );"
786        );
787
788        assert_eq!(format(input, &QueryParams::None, &options), expected);
789    }
790
791    #[test]
792    fn it_keep_long_parenthesized_lists_to_multiple_lines() {
793        let input = indoc!(
794            "
795            INSERT INTO some_table (id_product, id_shop, id_currency, id_country, id_registration) (
796            SELECT IF (dq.id_discounter_shopping = 2, dq.value, dq.value / 100),
797            IF (dq.id_discounter_shopping = 2, 'amount', 'percentage') FROM foo);"
798        );
799        let options = FormatOptions {
800            max_inline_block: 100,
801            ..Default::default()
802        };
803        let expected = indoc!(
804            "
805            INSERT INTO
806              some_table (id_product, id_shop, id_currency, id_country, id_registration) (
807                SELECT
808                  IF (dq.id_discounter_shopping = 2, dq.value, dq.value / 100),
809                  IF (dq.id_discounter_shopping = 2, 'amount', 'percentage')
810                FROM
811                  foo
812              );"
813        );
814
815        assert_eq!(format(input, &QueryParams::None, &options), expected);
816    }
817
818    #[test]
819    fn it_formats_simple_update_query() {
820        let input = "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';";
821        let options = FormatOptions::default();
822        let expected = indoc!(
823            "
824            UPDATE
825              Customers
826            SET
827              ContactName = 'Alfred Schmidt',
828              City = 'Hamburg'
829            WHERE
830              CustomerName = 'Alfreds Futterkiste';"
831        );
832
833        assert_eq!(format(input, &QueryParams::None, &options), expected);
834    }
835
836    #[test]
837    fn it_formats_simple_update_query_inlining_set() {
838        let input = "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';";
839        let options = FormatOptions {
840            max_inline_top_level: Some(20),
841            max_inline_arguments: Some(10),
842            ..Default::default()
843        };
844        let expected = indoc!(
845            "
846            UPDATE Customers SET
847              ContactName = 'Alfred Schmidt',
848              City = 'Hamburg'
849            WHERE
850              CustomerName = 'Alfreds Futterkiste';"
851        );
852
853        assert_eq!(format(input, &QueryParams::None, &options), expected);
854    }
855
856    #[test]
857    fn it_formats_simple_delete_query() {
858        let input = "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;";
859        let options = FormatOptions::default();
860        let expected = indoc!(
861            "
862            DELETE FROM
863              Customers
864            WHERE
865              CustomerName = 'Alfred'
866              AND Phone = 5002132;"
867        );
868
869        assert_eq!(format(input, &QueryParams::None, &options), expected);
870    }
871
872    #[test]
873    fn it_formats_full_delete_query() {
874        let input =
875            "DELETE FROM Customers USING Phonebook WHERE CustomerName='Alfred' AND Phone=5002132;";
876        let options = FormatOptions::default();
877        let expected = indoc!(
878            "
879            DELETE FROM
880              Customers
881            USING
882              Phonebook
883            WHERE
884              CustomerName = 'Alfred'
885              AND Phone = 5002132;"
886        );
887
888        assert_eq!(format(input, &QueryParams::None, &options), expected);
889    }
890
891    #[test]
892    fn it_formats_simple_drop_query() {
893        let input = "DROP TABLE IF EXISTS admin_role;";
894        let options = FormatOptions::default();
895
896        assert_eq!(format(input, &QueryParams::None, &options), input);
897    }
898
899    #[test]
900    fn it_formats_incomplete_query() {
901        let input = "SELECT count(";
902        let options = FormatOptions::default();
903        let expected = indoc!(
904            "
905            SELECT
906              count("
907        );
908
909        assert_eq!(format(input, &QueryParams::None, &options), expected);
910    }
911
912    #[test]
913    fn it_formats_query_that_ends_with_open_comment() {
914        let input = indoc!(
915            "
916            SELECT count(*)
917            /*Comment"
918        );
919        let options = FormatOptions::default();
920        let expected = indoc!(
921            "
922            SELECT
923              count(*)
924              /*Comment"
925        );
926
927        assert_eq!(format(input, &QueryParams::None, &options), expected);
928    }
929
930    #[test]
931    fn it_formats_update_query_with_as_part() {
932        let input = "UPDATE customers SET total_orders = order_summary.total  FROM ( SELECT * FROM bank) AS order_summary";
933        let options = FormatOptions::default();
934        let expected = indoc!(
935            "
936            UPDATE
937              customers
938            SET
939              total_orders = order_summary.total
940            FROM
941              (
942                SELECT
943                  *
944                FROM
945                  bank
946              ) AS order_summary"
947        );
948
949        assert_eq!(format(input, &QueryParams::None, &options), expected);
950    }
951
952    #[test]
953    fn it_formats_update_query_with_as_part_inline() {
954        let options = FormatOptions {
955            inline: true,
956            ..Default::default()
957        };
958        let expected = "UPDATE customers SET total_orders = order_summary.total FROM ( SELECT * FROM bank ) AS order_summary";
959        let input = indoc!(
960            "
961            UPDATE
962              customers
963            SET
964              total_orders = order_summary.total
965            FROM
966              (
967                SELECT
968                  *
969                FROM
970                  bank
971              ) AS order_summary"
972        );
973
974        assert_eq!(format(input, &QueryParams::None, &options), expected);
975    }
976
977    #[test]
978    fn it_formats_top_level_and_newline_multi_word_reserved_words_with_inconsistent_spacing() {
979        let input = "SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah";
980        let options = FormatOptions::default();
981        let expected = indoc!(
982            "
983            SELECT
984              *
985            FROM
986              foo
987              LEFT OUTER JOIN bar
988            ORDER BY
989              blah"
990        );
991
992        assert_eq!(format(input, &QueryParams::None, &options), expected);
993    }
994
995    #[test]
996    fn it_formats_long_double_parenthesized_queries_to_multiple_lines() {
997        let input = "((foo = '0123456789-0123456789-0123456789-0123456789'))";
998        let options = FormatOptions::default();
999        let expected = indoc!(
1000            "
1001            (
1002              (
1003                foo = '0123456789-0123456789-0123456789-0123456789'
1004              )
1005            )"
1006        );
1007
1008        assert_eq!(format(input, &QueryParams::None, &options), expected);
1009    }
1010
1011    #[test]
1012    fn it_formats_short_double_parenthesizes_queries_to_one_line() {
1013        let input = "((foo = 'bar'))";
1014        let options = FormatOptions::default();
1015
1016        assert_eq!(format(input, &QueryParams::None, &options), input);
1017    }
1018
1019    #[test]
1020    fn it_formats_single_char_operators() {
1021        let inputs = [
1022            "foo = bar",
1023            "foo < bar",
1024            "foo > bar",
1025            "foo + bar",
1026            "foo - bar",
1027            "foo * bar",
1028            "foo / bar",
1029            "foo % bar",
1030        ];
1031        let options = FormatOptions::default();
1032        for input in &inputs {
1033            assert_eq!(&format(input, &QueryParams::None, &options), input);
1034        }
1035    }
1036
1037    #[test]
1038    fn it_formats_multi_char_operators() {
1039        let inputs = [
1040            "foo != bar",
1041            "foo <> bar",
1042            "foo == bar",
1043            "foo || bar",
1044            "foo <= bar",
1045            "foo >= bar",
1046            "foo !< bar",
1047            "foo !> bar",
1048        ];
1049        let options = FormatOptions::default();
1050        for input in &inputs {
1051            assert_eq!(&format(input, &QueryParams::None, &options), input);
1052        }
1053    }
1054
1055    #[test]
1056    fn it_formats_logical_operators() {
1057        let inputs = [
1058            "foo ALL bar",
1059            "foo = ANY (1, 2, 3)",
1060            "EXISTS bar",
1061            "foo IN (1, 2, 3)",
1062            "foo LIKE 'hello%'",
1063            "foo IS NULL",
1064            "UNIQUE foo",
1065        ];
1066        let options = FormatOptions::default();
1067        for input in &inputs {
1068            assert_eq!(&format(input, &QueryParams::None, &options), input);
1069        }
1070    }
1071
1072    #[test]
1073    fn it_formats_and_or_operators() {
1074        let strings = [
1075            ("foo BETWEEN bar AND baz", "foo BETWEEN bar AND baz"),
1076            ("foo BETWEEN\nbar\nAND baz", "foo BETWEEN bar AND baz"),
1077            ("foo AND bar", "foo\nAND bar"),
1078            ("foo OR bar", "foo\nOR bar"),
1079        ];
1080        let options = FormatOptions::default();
1081        for (input, output) in &strings {
1082            assert_eq!(&format(input, &QueryParams::None, &options), output);
1083        }
1084    }
1085
1086    #[test]
1087    fn it_recognizes_strings() {
1088        let inputs = ["\"foo JOIN bar\"", "'foo JOIN bar'", "`foo JOIN bar`"];
1089        let options = FormatOptions::default();
1090        for input in &inputs {
1091            assert_eq!(&format(input, &QueryParams::None, &options), input);
1092        }
1093    }
1094
1095    #[test]
1096    fn it_recognizes_escaped_strings() {
1097        let inputs = [
1098            r#""foo \" JOIN bar""#,
1099            r#"'foo \' JOIN bar'"#,
1100            r#"`foo `` JOIN bar`"#,
1101            r#"'foo '' JOIN bar'"#,
1102            r#"'two households"'"#,
1103            r#"'two households'''"#,
1104            r#"E'alice'''"#,
1105        ];
1106        let options = FormatOptions::default();
1107        for input in &inputs {
1108            assert_eq!(&format(input, &QueryParams::None, &options), input);
1109        }
1110    }
1111
1112    #[test]
1113    fn it_formats_postgres_specific_operators() {
1114        let strings = [
1115            ("column::int", "column::int"),
1116            ("v->2", "v -> 2"),
1117            ("v->>2", "v ->> 2"),
1118            ("foo ~~ 'hello'", "foo ~~ 'hello'"),
1119            ("foo !~ 'hello'", "foo !~ 'hello'"),
1120            ("foo ~* 'hello'", "foo ~* 'hello'"),
1121            ("foo ~~* 'hello'", "foo ~~* 'hello'"),
1122            ("foo !~~ 'hello'", "foo !~~ 'hello'"),
1123            ("foo !~* 'hello'", "foo !~* 'hello'"),
1124            ("foo !~~* 'hello'", "foo !~~* 'hello'"),
1125        ];
1126        let options = FormatOptions::default();
1127        for (input, output) in &strings {
1128            assert_eq!(&format(input, &QueryParams::None, &options), output);
1129        }
1130    }
1131
1132    #[test]
1133    fn it_keeps_separation_between_multiple_statements() {
1134        let strings = [
1135            ("foo;bar;", "foo;\nbar;"),
1136            ("foo\n;bar;", "foo;\nbar;"),
1137            ("foo\n\n\n;bar;\n\n", "foo;\nbar;"),
1138        ];
1139        let options = FormatOptions::default();
1140        for (input, output) in &strings {
1141            assert_eq!(&format(input, &QueryParams::None, &options), output);
1142        }
1143
1144        let input = indoc!(
1145            "
1146            SELECT count(*),Column1 FROM Table1;
1147            SELECT count(*),Column1 FROM Table2;"
1148        );
1149        let options = FormatOptions::default();
1150        let expected = indoc!(
1151            "
1152            SELECT
1153              count(*),
1154              Column1
1155            FROM
1156              Table1;
1157            SELECT
1158              count(*),
1159              Column1
1160            FROM
1161              Table2;"
1162        );
1163
1164        assert_eq!(format(input, &QueryParams::None, &options), expected);
1165    }
1166
1167    #[test]
1168    fn it_formats_unicode_correctly() {
1169        let input = "SELECT test, тест FROM table;";
1170        let options = FormatOptions::default();
1171        let expected = indoc!(
1172            "
1173            SELECT
1174              test,
1175              тест
1176            FROM
1177              table;"
1178        );
1179
1180        assert_eq!(format(input, &QueryParams::None, &options), expected);
1181    }
1182
1183    #[test]
1184    fn it_converts_keywords_to_uppercase_when_option_passed_in() {
1185        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
1186        let options = FormatOptions {
1187            uppercase: Some(true),
1188            ..FormatOptions::default()
1189        };
1190        let expected = indoc!(
1191            "
1192            SELECT DISTINCT
1193              *
1194            FROM
1195              foo
1196              LEFT JOIN bar
1197            WHERE
1198              cola > 1
1199              AND colb = 3"
1200        );
1201
1202        assert_eq!(format(input, &QueryParams::None, &options), expected);
1203    }
1204
1205    #[test]
1206    fn it_line_breaks_between_queries_with_config() {
1207        let input = "SELECT * FROM foo; SELECT * FROM bar;";
1208        let options = FormatOptions {
1209            lines_between_queries: 2,
1210            ..FormatOptions::default()
1211        };
1212        let expected = indoc!(
1213            "
1214            SELECT
1215              *
1216            FROM
1217              foo;
1218
1219            SELECT
1220              *
1221            FROM
1222              bar;"
1223        );
1224
1225        assert_eq!(format(input, &QueryParams::None, &options), expected);
1226    }
1227
1228    #[test]
1229    fn it_correctly_indents_create_statement_after_select() {
1230        let input = indoc!(
1231            "
1232            SELECT * FROM test;
1233            CREATE TABLE TEST(id NUMBER NOT NULL, col1 VARCHAR2(20), col2 VARCHAR2(20));
1234        "
1235        );
1236        let options = FormatOptions::default();
1237        let expected = indoc!(
1238            "
1239            SELECT
1240              *
1241            FROM
1242              test;
1243            CREATE TABLE TEST(
1244              id NUMBER NOT NULL,
1245              col1 VARCHAR2(20),
1246              col2 VARCHAR2(20)
1247            );"
1248        );
1249
1250        assert_eq!(format(input, &QueryParams::None, &options), expected);
1251    }
1252
1253    #[test]
1254    fn it_formats_short_create_table() {
1255        let input = "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);";
1256        let options = FormatOptions::default();
1257
1258        assert_eq!(format(input, &QueryParams::None, &options), input);
1259    }
1260
1261    #[test]
1262    fn it_formats_long_create_table() {
1263        let input =
1264            "CREATE TABLE items (a INT PRIMARY KEY, b TEXT, c INT NOT NULL, d INT NOT NULL);";
1265        let options = FormatOptions::default();
1266        let expected = indoc!(
1267            "
1268            CREATE TABLE items (
1269              a INT PRIMARY KEY,
1270              b TEXT,
1271              c INT NOT NULL,
1272              d INT NOT NULL
1273            );"
1274        );
1275
1276        assert_eq!(format(input, &QueryParams::None, &options), expected);
1277    }
1278
1279    #[test]
1280    fn it_formats_insert_without_into() {
1281        let input =
1282            "INSERT Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');";
1283        let options = FormatOptions::default();
1284        let expected = indoc!(
1285            "
1286            INSERT
1287              Customers (ID, MoneyBalance, Address, City)
1288            VALUES
1289              (12, -123.4, 'Skagen 2111', 'Stv');"
1290        );
1291
1292        assert_eq!(format(input, &QueryParams::None, &options), expected);
1293    }
1294
1295    #[test]
1296    fn it_formats_alter_table_modify_query() {
1297        let input = "ALTER TABLE supplier MODIFY supplier_name char(100) NOT NULL;";
1298        let options = FormatOptions::default();
1299        let expected = indoc!(
1300            "
1301            ALTER TABLE
1302              supplier
1303            MODIFY
1304              supplier_name char(100) NOT NULL;"
1305        );
1306
1307        assert_eq!(format(input, &QueryParams::None, &options), expected);
1308    }
1309
1310    #[test]
1311    fn it_formats_alter_table_alter_column_query() {
1312        let input = "ALTER TABLE supplier ALTER COLUMN supplier_name VARCHAR(100) NOT NULL;";
1313        let options = FormatOptions::default();
1314        let expected = indoc!(
1315            "
1316            ALTER TABLE
1317              supplier
1318            ALTER COLUMN
1319              supplier_name VARCHAR(100) NOT NULL;"
1320        );
1321
1322        assert_eq!(format(input, &QueryParams::None, &options), expected);
1323    }
1324
1325    #[test]
1326    fn it_recognizes_bracketed_strings() {
1327        let inputs = ["[foo JOIN bar]", "[foo ]] JOIN bar]"];
1328        let options = FormatOptions::default();
1329        for input in &inputs {
1330            assert_eq!(&format(input, &QueryParams::None, &options), input);
1331        }
1332    }
1333
1334    #[test]
1335    fn it_recognizes_at_variables() {
1336        let input =
1337            "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name];";
1338        let options = FormatOptions::default();
1339        let expected = indoc!(
1340            "
1341            SELECT
1342              @variable,
1343              @a1_2.3$,
1344              @'var name',
1345              @\"var name\",
1346              @`var name`,
1347              @[var name];"
1348        );
1349
1350        assert_eq!(format(input, &QueryParams::None, &options), expected);
1351    }
1352
1353    #[test]
1354    fn it_recognizes_at_variables_with_param_values() {
1355        let input =
1356            "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name], @'var\\name';";
1357        let params = vec![
1358            ("variable".to_string(), "\"variable value\"".to_string()),
1359            ("a1_2.3$".to_string(), "'weird value'".to_string()),
1360            ("var name".to_string(), "'var value'".to_string()),
1361            ("var\\name".to_string(), "'var\\ value'".to_string()),
1362        ];
1363        let options = FormatOptions::default();
1364        let expected = indoc!(
1365            "
1366            SELECT
1367              \"variable value\",
1368              'weird value',
1369              'var value',
1370              'var value',
1371              'var value',
1372              'var value',
1373              'var\\ value';"
1374        );
1375
1376        assert_eq!(
1377            format(input, &QueryParams::Named(params), &options),
1378            expected
1379        );
1380    }
1381
1382    #[test]
1383    fn it_recognizes_colon_variables() {
1384        let input =
1385            "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`, :[var name];";
1386        let options = FormatOptions::default();
1387        let expected = indoc!(
1388            "
1389            SELECT
1390              :variable,
1391              :a1_2.3$,
1392              :'var name',
1393              :\"var name\",
1394              :`var name`,
1395              :[var name];"
1396        );
1397
1398        assert_eq!(format(input, &QueryParams::None, &options), expected);
1399    }
1400
1401    #[test]
1402    fn it_recognizes_colon_variables_with_param_values() {
1403        let input = indoc!(
1404            "
1405            SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`,
1406            :[var name], :'escaped \\'var\\'', :\"^*& weird \\\" var   \";
1407            "
1408        );
1409        let params = vec![
1410            ("variable".to_string(), "\"variable value\"".to_string()),
1411            ("a1_2.3$".to_string(), "'weird value'".to_string()),
1412            ("var name".to_string(), "'var value'".to_string()),
1413            ("escaped 'var'".to_string(), "'weirder value'".to_string()),
1414            (
1415                "^*& weird \" var   ".to_string(),
1416                "'super weird value'".to_string(),
1417            ),
1418        ];
1419        let options = FormatOptions::default();
1420        let expected = indoc!(
1421            "
1422            SELECT
1423              \"variable value\",
1424              'weird value',
1425              'var value',
1426              'var value',
1427              'var value',
1428              'var value',
1429              'weirder value',
1430              'super weird value';"
1431        );
1432
1433        assert_eq!(
1434            format(input, &QueryParams::Named(params), &options),
1435            expected
1436        );
1437    }
1438
1439    #[test]
1440    fn it_recognizes_question_numbered_placeholders() {
1441        let input = "SELECT ?1, ?25, ?;";
1442        let options = FormatOptions::default();
1443        let expected = indoc!(
1444            "
1445            SELECT
1446              ?1,
1447              ?25,
1448              ?;"
1449        );
1450
1451        assert_eq!(format(input, &QueryParams::None, &options), expected);
1452    }
1453
1454    #[test]
1455    fn it_recognizes_question_numbered_placeholders_with_param_values() {
1456        let input = "SELECT ?1, ?2, ?0;";
1457        let params = vec![
1458            "first".to_string(),
1459            "second".to_string(),
1460            "third".to_string(),
1461        ];
1462        let options = FormatOptions::default();
1463        let expected = indoc!(
1464            "
1465            SELECT
1466              second,
1467              third,
1468              first;"
1469        );
1470
1471        assert_eq!(
1472            format(input, &QueryParams::Indexed(params), &options),
1473            expected
1474        );
1475
1476        format("?62666666121266666612", &QueryParams::None, &options);
1477    }
1478
1479    #[test]
1480    fn it_recognizes_question_indexed_placeholders_with_param_values() {
1481        let input = "SELECT ?, ?, ?;";
1482        let params = vec![
1483            "first".to_string(),
1484            "second".to_string(),
1485            "third".to_string(),
1486        ];
1487        let options = FormatOptions::default();
1488        let expected = indoc!(
1489            "
1490            SELECT
1491              first,
1492              second,
1493              third;"
1494        );
1495
1496        assert_eq!(
1497            format(input, &QueryParams::Indexed(params), &options),
1498            expected
1499        );
1500    }
1501
1502    #[test]
1503    fn it_recognizes_dollar_sign_numbered_placeholders() {
1504        let input = "SELECT $1, $2;";
1505        let options = FormatOptions::default();
1506        let expected = indoc!(
1507            "
1508            SELECT
1509              $1,
1510              $2;"
1511        );
1512
1513        assert_eq!(format(input, &QueryParams::None, &options), expected);
1514    }
1515
1516    #[test]
1517    fn it_recognizes_dollar_sign_alphanumeric_placeholders() {
1518        let input = "SELECT $hash, $foo, $bar;";
1519        let options = FormatOptions::default();
1520        let expected = indoc!(
1521            "
1522            SELECT
1523              $hash,
1524              $foo,
1525              $bar;"
1526        );
1527
1528        assert_eq!(format(input, &QueryParams::None, &options), expected);
1529    }
1530
1531    #[test]
1532    fn it_recognizes_dollar_sign_numbered_placeholders_with_param_values() {
1533        let input = "SELECT $2, $3, $1, $named, $4, $alias;";
1534        let params = vec![
1535            "first".to_string(),
1536            "second".to_string(),
1537            "third".to_string(),
1538            "4th".to_string(),
1539        ];
1540        let options = FormatOptions::default();
1541        let expected = indoc!(
1542            "
1543            SELECT
1544              second,
1545              third,
1546              first,
1547              $named,
1548              4th,
1549              $alias;"
1550        );
1551
1552        assert_eq!(
1553            format(input, &QueryParams::Indexed(params), &options),
1554            expected
1555        );
1556    }
1557
1558    #[test]
1559    fn it_recognizes_dollar_sign_alphanumeric_placeholders_with_param_values() {
1560        let input = "SELECT $hash, $salt, $1, $2;";
1561        let params = vec![
1562            ("hash".to_string(), "hash value".to_string()),
1563            ("salt".to_string(), "salt value".to_string()),
1564            ("1".to_string(), "number 1".to_string()),
1565            ("2".to_string(), "number 2".to_string()),
1566        ];
1567        let options = FormatOptions::default();
1568        let expected = indoc!(
1569            "
1570            SELECT
1571              hash value,
1572              salt value,
1573              number 1,
1574              number 2;"
1575        );
1576
1577        assert_eq!(
1578            format(input, &QueryParams::Named(params), &options),
1579            expected
1580        );
1581    }
1582
1583    #[test]
1584    fn it_formats_query_with_go_batch_separator() {
1585        let input = "SELECT 1 GO SELECT 2";
1586        let params = vec![
1587            "first".to_string(),
1588            "second".to_string(),
1589            "third".to_string(),
1590        ];
1591        let options = FormatOptions::default();
1592        let expected = indoc!(
1593            "
1594            SELECT
1595              1
1596            GO
1597            SELECT
1598              2"
1599        );
1600
1601        assert_eq!(
1602            format(input, &QueryParams::Indexed(params), &options),
1603            expected
1604        );
1605    }
1606
1607    #[test]
1608    fn it_formats_select_query_with_cross_join() {
1609        let input = "SELECT a, b FROM t CROSS JOIN t2 on t.id = t2.id_t";
1610        let options = FormatOptions::default();
1611        let expected = indoc!(
1612            "
1613            SELECT
1614              a,
1615              b
1616            FROM
1617              t
1618              CROSS JOIN t2 on t.id = t2.id_t"
1619        );
1620
1621        assert_eq!(format(input, &QueryParams::None, &options), expected);
1622    }
1623
1624    #[test]
1625    fn it_formats_select_query_with_cross_apply() {
1626        let input = "SELECT a, b FROM t CROSS APPLY fn(t.id)";
1627        let options = FormatOptions::default();
1628        let expected = indoc!(
1629            "
1630            SELECT
1631              a,
1632              b
1633            FROM
1634              t
1635              CROSS APPLY fn(t.id)"
1636        );
1637
1638        assert_eq!(format(input, &QueryParams::None, &options), expected);
1639    }
1640
1641    #[test]
1642    fn it_formats_simple_select() {
1643        let input = "SELECT N, M FROM t";
1644        let options = FormatOptions::default();
1645        let expected = indoc!(
1646            "
1647            SELECT
1648              N,
1649              M
1650            FROM
1651              t"
1652        );
1653
1654        assert_eq!(format(input, &QueryParams::None, &options), expected);
1655    }
1656
1657    #[test]
1658    fn it_formats_simple_select_with_national_characters_mssql() {
1659        let input = "SELECT N'value'";
1660        let options = FormatOptions::default();
1661        let expected = indoc!(
1662            "
1663            SELECT
1664              N'value'"
1665        );
1666
1667        assert_eq!(format(input, &QueryParams::None, &options), expected);
1668    }
1669
1670    #[test]
1671    fn it_formats_select_query_with_outer_apply() {
1672        let input = "SELECT a, b FROM t OUTER APPLY fn(t.id)";
1673        let options = FormatOptions::default();
1674        let expected = indoc!(
1675            "
1676            SELECT
1677              a,
1678              b
1679            FROM
1680              t
1681              OUTER APPLY fn(t.id)"
1682        );
1683
1684        assert_eq!(format(input, &QueryParams::None, &options), expected);
1685    }
1686
1687    #[test]
1688    fn it_formats_fetch_first_like_limit() {
1689        let input = "SELECT * FETCH FIRST 2 ROWS ONLY;";
1690        let options = FormatOptions::default();
1691        let expected = indoc!(
1692            "
1693            SELECT
1694              *
1695            FETCH FIRST
1696              2 ROWS ONLY;"
1697        );
1698
1699        assert_eq!(format(input, &QueryParams::None, &options), expected);
1700    }
1701
1702    #[test]
1703    fn it_formats_case_when_with_a_blank_expression() {
1704        let input = "CASE WHEN option = 'foo' THEN 1 WHEN option = 'bar' THEN 2 WHEN option = 'baz' THEN 3 ELSE 4 END;";
1705        let options = FormatOptions::default();
1706        let expected = indoc!(
1707            "
1708            CASE
1709              WHEN option = 'foo' THEN 1
1710              WHEN option = 'bar' THEN 2
1711              WHEN option = 'baz' THEN 3
1712              ELSE 4
1713            END;"
1714        );
1715
1716        assert_eq!(format(input, &QueryParams::None, &options), expected);
1717    }
1718
1719    #[test]
1720    fn it_formats_case_when_inside_select() {
1721        let input =
1722            "SELECT foo, bar, CASE baz WHEN 'one' THEN 1 WHEN 'two' THEN 2 ELSE 3 END FROM table";
1723        let options = FormatOptions::default();
1724        let expected = indoc!(
1725            "
1726            SELECT
1727              foo,
1728              bar,
1729              CASE
1730                baz
1731                WHEN 'one' THEN 1
1732                WHEN 'two' THEN 2
1733                ELSE 3
1734              END
1735            FROM
1736              table"
1737        );
1738
1739        assert_eq!(format(input, &QueryParams::None, &options), expected);
1740    }
1741
1742    #[test]
1743    fn it_formats_case_when_inside_select_inlined_top_level() {
1744        let input =
1745            "SELECT foo, bar, CASE baz WHEN 'one' THEN 1 WHEN 'two' THEN 2 ELSE 3 END FROM table";
1746        let options = FormatOptions {
1747            max_inline_top_level: Some(50),
1748            ..Default::default()
1749        };
1750        let expected = indoc!(
1751            "
1752            SELECT
1753              foo,
1754              bar,
1755              CASE
1756                baz
1757                WHEN 'one' THEN 1
1758                WHEN 'two' THEN 2
1759                ELSE 3
1760              END
1761            FROM table"
1762        );
1763
1764        assert_eq!(format(input, &QueryParams::None, &options), expected);
1765    }
1766
1767    #[test]
1768    fn it_formats_case_when_with_an_expression() {
1769        let input = "CASE toString(getNumber()) WHEN 'one' THEN 1 WHEN 'two' THEN 2 WHEN 'three' THEN 3 ELSE 4 END;";
1770        let options = FormatOptions::default();
1771        let expected = indoc!(
1772            "
1773            CASE
1774              toString(getNumber())
1775              WHEN 'one' THEN 1
1776              WHEN 'two' THEN 2
1777              WHEN 'three' THEN 3
1778              ELSE 4
1779            END;"
1780        );
1781
1782        assert_eq!(format(input, &QueryParams::None, &options), expected);
1783    }
1784
1785    #[test]
1786    fn it_formats_case_when_inside_an_order_by() {
1787        let input = "SELECT a, created_at FROM b ORDER BY (CASE $3 WHEN 'created_at_asc' THEN created_at END) ASC, (CASE $3 WHEN 'created_at_desc' THEN created_at END) DESC;";
1788        let max_line = 80;
1789        let options = FormatOptions {
1790            max_inline_block: max_line,
1791            max_inline_arguments: Some(max_line),
1792            ..Default::default()
1793        };
1794
1795        let expected = indoc!(
1796            "
1797            SELECT
1798              a, created_at
1799            FROM
1800              b
1801            ORDER BY
1802              (CASE $3 WHEN 'created_at_asc' THEN created_at END) ASC,
1803              (CASE $3 WHEN 'created_at_desc' THEN created_at END) DESC;"
1804        );
1805
1806        assert_eq!(format(input, &QueryParams::None, &options), expected);
1807    }
1808
1809    #[test]
1810    fn it_recognizes_lowercase_case_end() {
1811        let input = "case when option = 'foo' then 1 else 2 end;";
1812        let options = FormatOptions::default();
1813        let expected = indoc!(
1814            "
1815            case
1816              when option = 'foo' then 1
1817              else 2
1818            end;"
1819        );
1820
1821        assert_eq!(format(input, &QueryParams::None, &options), expected);
1822    }
1823
1824    #[test]
1825    fn it_ignores_words_case_and_end_inside_other_strings() {
1826        let input = "SELECT CASEDATE, ENDDATE FROM table1;";
1827        let options = FormatOptions::default();
1828        let expected = indoc!(
1829            "
1830            SELECT
1831              CASEDATE,
1832              ENDDATE
1833            FROM
1834              table1;"
1835        );
1836
1837        assert_eq!(format(input, &QueryParams::None, &options), expected);
1838    }
1839
1840    #[test]
1841    fn it_formats_tricky_line_comments() {
1842        let input = "SELECT a#comment, here\nFROM b--comment";
1843        let options = FormatOptions::default();
1844        let expected = indoc!(
1845            "
1846            SELECT
1847              a #comment, here
1848            FROM
1849              b --comment"
1850        );
1851
1852        assert_eq!(format(input, &QueryParams::None, &options), expected);
1853    }
1854
1855    #[test]
1856    fn it_formats_line_comments_followed_by_semicolon() {
1857        let input = indoc!(
1858            "
1859            SELECT a FROM b
1860            --comment
1861            ;"
1862        );
1863        let options = FormatOptions::default();
1864        let expected = indoc!(
1865            "
1866            SELECT
1867              a
1868            FROM
1869              b --comment
1870            ;"
1871        );
1872
1873        assert_eq!(format(input, &QueryParams::None, &options), expected);
1874    }
1875
1876    #[test]
1877    fn it_formats_line_comments_followed_by_comma() {
1878        let input = indoc!(
1879            "
1880            SELECT a --comment
1881            , b"
1882        );
1883        let options = FormatOptions::default();
1884        let expected = indoc!(
1885            "
1886            SELECT
1887              a --comment
1888            ,
1889              b"
1890        );
1891
1892        assert_eq!(format(input, &QueryParams::None, &options), expected);
1893    }
1894
1895    #[test]
1896    fn it_formats_line_comments_followed_by_close_paren() {
1897        let input = "SELECT ( a --comment\n )";
1898        let options = FormatOptions::default();
1899        let expected = indoc!(
1900            "
1901            SELECT
1902              (
1903                a --comment
1904              )"
1905        );
1906
1907        assert_eq!(format(input, &QueryParams::None, &options), expected);
1908    }
1909
1910    #[test]
1911    fn it_formats_line_comments_followed_by_open_paren() {
1912        let input = "SELECT a --comment\n()";
1913        let options = FormatOptions::default();
1914        let expected = indoc!(
1915            "
1916            SELECT
1917              a --comment
1918              ()"
1919        );
1920
1921        assert_eq!(format(input, &QueryParams::None, &options), expected);
1922    }
1923
1924    #[test]
1925    fn it_formats_lonely_semicolon() {
1926        let input = ";";
1927        let options = FormatOptions::default();
1928
1929        assert_eq!(format(input, &QueryParams::None, &options), input);
1930    }
1931
1932    #[test]
1933    fn it_formats_multibyte_chars() {
1934        let input = "\nSELECT 'главная'";
1935        let options = FormatOptions::default();
1936        let expected = "SELECT\n  'главная'";
1937
1938        assert_eq!(format(input, &QueryParams::None, &options), expected);
1939    }
1940
1941    #[test]
1942    fn it_recognizes_scientific_notation() {
1943        let input = "SELECT *, 1e-7 as small, 1e2 as medium, 1e+7 as large FROM t";
1944        let options = FormatOptions::default();
1945        let expected = indoc!(
1946            "
1947            SELECT
1948              *,
1949              1e-7 as small,
1950              1e2 as medium,
1951              1e+7 as large
1952            FROM
1953              t"
1954        );
1955
1956        assert_eq!(format(input, &QueryParams::None, &options), expected);
1957    }
1958
1959    #[test]
1960    fn it_keeps_double_dollar_signs_together() {
1961        let input = "CREATE FUNCTION abc() AS $$ SELECT * FROM table $$ LANGUAGE plpgsql;";
1962        let options = FormatOptions::default();
1963        let expected = indoc!(
1964            "
1965            CREATE FUNCTION abc() AS
1966            $$
1967            SELECT
1968              *
1969            FROM
1970              table
1971            $$
1972            LANGUAGE plpgsql;"
1973        );
1974
1975        assert_eq!(format(input, &QueryParams::None, &options), expected);
1976    }
1977
1978    #[test]
1979    fn it_formats_pgplsql() {
1980        let input = "CREATE FUNCTION abc() AS $$ DECLARE a int := 1; b int := 2; BEGIN SELECT * FROM table $$ LANGUAGE plpgsql;";
1981        let options = FormatOptions::default();
1982        let expected = indoc!(
1983            "
1984            CREATE FUNCTION abc() AS
1985            $$
1986            DECLARE
1987            a int := 1;
1988            b int := 2;
1989            BEGIN
1990            SELECT
1991              *
1992            FROM
1993              table
1994            $$
1995            LANGUAGE plpgsql;"
1996        );
1997
1998        assert_eq!(format(input, &QueryParams::None, &options), expected);
1999    }
2000
2001    #[test]
2002    fn it_handles_comments_correctly() {
2003        let input = indoc!(
2004            "
2005                -- 创建一个外部表,存储销售数据
2006            CREATE EXTERNAL TABLE IF NOT EXISTS sales_data (
2007                -- 唯一标识订单ID
2008                order_id BIGINT COMMENT 'Unique identifier for the order',
2009
2010                -- 客户ID
2011                customer_id BIGINT COMMENT 'Unique identifier for the customer',
2012            )
2013            COMMENT 'Sales data table for storing transaction records';
2014
2015            -- 按销售日期和城市进行分区
2016            PARTITIONED BY (
2017                sale_year STRING COMMENT 'Year of the sale',
2018                sale_month STRING COMMENT 'Month of the sale'
2019            )
2020
2021            -- 设置数据存储位置
2022            LOCATION '/user/hive/warehouse/sales_data'
2023
2024            -- 使用 ORC 存储格式
2025            STORED AS ORC
2026
2027            -- 设置表的行格式
2028            ROW FORMAT DELIMITED
2029            FIELDS TERMINATED BY ','
2030            LINES TERMINATED BY '\n'
2031
2032            -- 设置表属性
2033            TBLPROPERTIES (
2034                'orc.compress' = 'SNAPPY',          -- 使用SNAPPY压缩
2035                'transactional' = 'true',           -- 启用事务支持
2036                'orc.create.index' = 'true',        -- 创建索引
2037                'skip.header.line.count' = '1',     -- 跳过CSV文件的第一行
2038                'external.table.purge' = 'true'     -- 在删除表时自动清理数据
2039            );
2040
2041            -- 自动加载数据到 Hive 分区中
2042            ALTER TABLE sales_data
2043            ADD PARTITION (sale_year = '2024', sale_month = '08')
2044            LOCATION '/user/hive/warehouse/sales_data/2024/08';"
2045        );
2046        let options = FormatOptions {
2047            indent: Indent::Spaces(4),
2048            ..Default::default()
2049        };
2050        let expected = indoc!(
2051            "
2052            -- 创建一个外部表,存储销售数据
2053            CREATE EXTERNAL TABLE IF NOT EXISTS sales_data (
2054                -- 唯一标识订单ID
2055                order_id BIGINT COMMENT 'Unique identifier for the order',
2056                -- 客户ID
2057                customer_id BIGINT COMMENT 'Unique identifier for the customer',
2058            ) COMMENT 'Sales data table for storing transaction records';
2059            -- 按销售日期和城市进行分区
2060            PARTITIONED BY (
2061                sale_year STRING COMMENT 'Year of the sale',
2062                sale_month STRING COMMENT 'Month of the sale'
2063            )
2064            -- 设置数据存储位置
2065            LOCATION '/user/hive/warehouse/sales_data'
2066            -- 使用 ORC 存储格式
2067            STORED AS ORC
2068            -- 设置表的行格式
2069            ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
2070            -- 设置表属性
2071            TBLPROPERTIES (
2072                'orc.compress' = 'SNAPPY',  -- 使用SNAPPY压缩
2073                'transactional' = 'true',  -- 启用事务支持
2074                'orc.create.index' = 'true',  -- 创建索引
2075                'skip.header.line.count' = '1',  -- 跳过CSV文件的第一行
2076                'external.table.purge' = 'true' -- 在删除表时自动清理数据
2077            );
2078            -- 自动加载数据到 Hive 分区中
2079            ALTER TABLE
2080                sales_data
2081            ADD
2082                PARTITION (sale_year = '2024', sale_month = '08') LOCATION '/user/hive/warehouse/sales_data/2024/08';"
2083        );
2084
2085        assert_eq!(format(input, &QueryParams::None, &options), expected);
2086    }
2087
2088    #[test]
2089    fn it_recognizes_returning_clause() {
2090        let input = indoc!(
2091            "
2092          INSERT INTO
2093            users (name, email)
2094          VALUES
2095            ($1, $2) RETURNING name,
2096            email"
2097        );
2098        let options = FormatOptions::default();
2099        let expected = indoc!(
2100            "
2101          INSERT INTO
2102            users (name, email)
2103          VALUES
2104            ($1, $2)
2105          RETURNING
2106            name,
2107            email"
2108        );
2109
2110        assert_eq!(format(input, &QueryParams::None, &options), expected);
2111    }
2112
2113    #[test]
2114    fn it_recognizes_on_update_clause() {
2115        let input = indoc!(
2116            "CREATE TABLE a (b integer REFERENCES c (id) ON                                     UPDATE RESTRICT, other integer);"
2117        );
2118        let options = FormatOptions::default();
2119        let expected = indoc!(
2120            "
2121          CREATE TABLE a (
2122            b integer REFERENCES c (id) ON UPDATE RESTRICT,
2123            other integer
2124          );"
2125        );
2126        assert_eq!(format(input, &QueryParams::None, &options), expected);
2127    }
2128
2129    #[test]
2130    fn it_formats_except_on_columns() {
2131        let input = indoc!(
2132            "SELECT table_0.* EXCEPT (profit),
2133                    details.* EXCEPT (item_id),
2134                    table_0.profit
2135        FROM  table_0"
2136        );
2137        let options = FormatOptions {
2138            indent: Indent::Spaces(4),
2139            ..Default::default()
2140        };
2141        let expected = indoc!(
2142            "
2143            SELECT
2144                table_0.* EXCEPT (profit),
2145                details.* EXCEPT (item_id),
2146                table_0.profit
2147            FROM
2148                table_0"
2149        );
2150
2151        assert_eq!(format(input, &QueryParams::None, &options), expected);
2152    }
2153
2154    #[test]
2155    fn it_uses_given_ignore_case_convert_config() {
2156        let input = "select count(*),Column1 from Table1;";
2157        let options = FormatOptions {
2158            uppercase: Some(true),
2159            ignore_case_convert: Some(vec!["from"]),
2160            ..FormatOptions::default()
2161        };
2162        let expected = indoc!(
2163            "
2164            SELECT
2165              count(*),
2166              Column1
2167            from
2168              Table1;"
2169        );
2170
2171        assert_eq!(format(input, &QueryParams::None, &options), expected);
2172    }
2173
2174    #[test]
2175    fn it_recognizes_fmt_off() {
2176        let input = indoc!(
2177            "SELECT              *     FROM   sometable
2178            WHERE
2179            -- comment test here
2180                 -- fmt: off
2181                first_key.second_key = 1
2182                                -- json:first_key.second_key = 1
2183                      -- fmt: on
2184                AND
2185                   -- fm1t: off
2186                first_key.second_key = 1
2187                                    --  json:first_key.second_key = 1
2188                -- fmt:on"
2189        );
2190        let options = FormatOptions {
2191            indent: Indent::Spaces(4),
2192            ..Default::default()
2193        };
2194        let expected = indoc!(
2195            "
2196            SELECT
2197                *
2198            FROM
2199                sometable
2200            WHERE
2201                -- comment test here
2202                first_key.second_key = 1
2203                                -- json:first_key.second_key = 1
2204                AND
2205                -- fm1t: off
2206                first_key.second_key = 1
2207                --  json:first_key.second_key = 1"
2208        );
2209
2210        assert_eq!(format(input, &QueryParams::None, &options), expected);
2211    }
2212
2213    #[test]
2214    fn it_converts_keywords_to_lowercase_when_option_passed_in() {
2215        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
2216        let options = FormatOptions {
2217            uppercase: Some(false),
2218            ..FormatOptions::default()
2219        };
2220        let expected = indoc!(
2221            "
2222            select distinct
2223              *
2224            from
2225              foo
2226              left join bar
2227            where
2228              cola > 1
2229              and colb = 3"
2230        );
2231
2232        assert_eq!(format(input, &QueryParams::None, &options), expected);
2233    }
2234
2235    #[test]
2236    fn format_nested_select() {
2237        let input = "WITH a AS ( SELECT a, b, c FROM t WHERE a > 100 ), aa AS ( SELECT field FROM table ) SELECT b, field FROM a, aa;";
2238        let options = FormatOptions {
2239            max_inline_arguments: Some(10),
2240            max_inline_top_level: Some(9),
2241            ..Default::default()
2242        };
2243        let expected = indoc! {
2244            "
2245            WITH
2246            a AS (
2247              SELECT a, b, c
2248              FROM t
2249              WHERE a > 100
2250            ),
2251            aa AS (
2252              SELECT field
2253              FROM table
2254            )
2255            SELECT
2256              b,
2257              field
2258            FROM a, aa;"
2259        };
2260        assert_eq!(format(input, &QueryParams::None, &options), expected);
2261    }
2262
2263    #[test]
2264    fn format_short_with() {
2265        let input = "WITH a AS ( SELECT a, b, c FROM t WHERE a > 100 ) SELECT b, field FROM a, aa;";
2266        let max_line = 80;
2267        let options = FormatOptions {
2268            max_inline_block: max_line,
2269            max_inline_arguments: Some(max_line),
2270            max_inline_top_level: Some(max_line),
2271            joins_as_top_level: true,
2272            ..Default::default()
2273        };
2274        let expected = indoc! {
2275            "
2276            WITH a AS (SELECT a, b, c FROM t WHERE a > 100)
2277            SELECT b, field
2278            FROM a, aa;"
2279        };
2280        assert_eq!(format(input, &QueryParams::None, &options), expected);
2281    }
2282
2283    #[test]
2284    fn format_nested_select_nested_blocks() {
2285        let input =
2286            "WITH a AS ( SELECT a, b, c FROM t WHERE a > 100 ), aa AS ( SELECT field FROM table ),
2287            bb AS ( SELECT count(*) as c FROM d ), cc AS ( INSERT INTO C (a, b, c, d) VALUES (1 ,2 ,3 ,4) )
2288        SELECT b, field FROM a, aa;";
2289        let max_line = 20;
2290        let options = FormatOptions {
2291            max_inline_block: max_line,
2292            max_inline_arguments: Some(max_line),
2293            max_inline_top_level: Some(max_line / 2),
2294            joins_as_top_level: true,
2295            ..Default::default()
2296        };
2297        let expected = indoc! {
2298            "
2299            WITH
2300            a AS (
2301              SELECT a, b, c
2302              FROM t
2303              WHERE a > 100
2304            ),
2305            aa AS (
2306              SELECT field
2307              FROM table
2308            ),
2309            bb AS (
2310              SELECT
2311                count(*) as c
2312              FROM d
2313            ),
2314            cc AS (
2315              INSERT INTO
2316                C (a, b, c, d)
2317              VALUES
2318                (1, 2, 3, 4)
2319            )
2320            SELECT b, field
2321            FROM a, aa;"
2322        };
2323        assert_eq!(format(input, &QueryParams::None, &options), expected);
2324    }
2325
2326    #[test]
2327    fn it_converts_keywords_nothing_when_no_option_passed_in() {
2328        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
2329        let options = FormatOptions {
2330            uppercase: None,
2331            ..FormatOptions::default()
2332        };
2333        let expected = indoc!(
2334            "
2335            select distinct
2336              *
2337            frOM
2338              foo
2339              left join bar
2340            WHERe
2341              cola > 1
2342              and colb = 3"
2343        );
2344
2345        assert_eq!(format(input, &QueryParams::None, &options), expected);
2346    }
2347    #[test]
2348    fn it_correctly_parses_all_operators() {
2349        let operators = [
2350            "!!", "!~~*", "!~~", "!~*", "!~", "##", "#>>", "#>", "#-", "&<|", "&<", "&>", "&&",
2351            "*<>", "*<=", "*>=", "*>", "*=", "*<", "<<|", "<<=", "<<", "<->", "<@", "<^", "<=",
2352            "<>", "<", ">=", ">>=", ">>", ">^", "->>", "->", "-|-", "-", "+", "/", "=", "%", "?||",
2353            "?|", "?-|", "?-", "?#", "?&", "?", "@@@", "@@", "@>", "@?", "@-@", "@", "^@", "^",
2354            "|&>", "|>>", "|/", "|", "||/", "||", "~>=~", "~>~", "~<=~", "~<~", "~=", "~*", "~~*",
2355            "~~", "~", "%", "<%", "%>", "<<%", "%>>", "<<->", "<->>", "<<<->", "<->>>",
2356        ];
2357
2358        // Test each operator individually
2359        for &operator in &operators {
2360            let input = format!("left {} right", operator);
2361            let expected = format!("left {} right", operator);
2362            let options = FormatOptions {
2363                uppercase: None,
2364                ..FormatOptions::default()
2365            };
2366
2367            assert_eq!(
2368                format(&input, &QueryParams::None, &options),
2369                expected,
2370                "Failed to parse operator: {}",
2371                operator
2372            );
2373        }
2374    }
2375    #[test]
2376    fn it_correctly_splits_operators() {
2377        let input = "
2378  SELECT
2379  left <@ right,
2380  left << right,
2381  left >> right,
2382  left &< right,
2383  left &> right,
2384  left -|- right,
2385  @@ left,
2386  @-@ left,
2387  left <-> right,
2388  left <<| right,
2389  left |>> right,
2390  left &<| right,
2391  left |>& right,
2392  left <^ right,
2393  left >^ right,
2394  left <% right,
2395  left %> right,
2396  ?- left,
2397  left ?-| right,
2398  left ?|| right,
2399  left ~= right";
2400        let options = FormatOptions {
2401            uppercase: None,
2402            ..FormatOptions::default()
2403        };
2404        let expected = indoc!(
2405            "
2406SELECT
2407  left <@ right,
2408  left << right,
2409  left >> right,
2410  left &< right,
2411  left &> right,
2412  left -|- right,
2413  @@ left,
2414  @-@ left,
2415  left <-> right,
2416  left <<| right,
2417  left |>> right,
2418  left &<| right,
2419  left |>& right,
2420  left <^ right,
2421  left >^ right,
2422  left <% right,
2423  left %> right,
2424  ?- left,
2425  left ?-| right,
2426  left ?|| right,
2427  left ~= right"
2428        );
2429
2430        assert_eq!(format(input, &QueryParams::None, &options), expected);
2431    }
2432    #[test]
2433    fn it_formats_double_colons() {
2434        let input = "select text  ::  text, num::integer, data::json, (x - y)::integer  frOM foo";
2435        let options = FormatOptions {
2436            uppercase: Some(false),
2437            ..FormatOptions::default()
2438        };
2439        let expected = indoc!(
2440            "
2441select
2442  text::text,
2443  num::integer,
2444  data::json,
2445  (x - y)::integer
2446from
2447  foo"
2448        );
2449
2450        assert_eq!(format(input, &QueryParams::None, &options), expected);
2451    }
2452
2453    #[test]
2454    fn it_formats_blocks_inline_or_not() {
2455        let input = " UPDATE t
2456
2457
2458        SET o = ($5 + $6 + $7 + $8),a = CASE WHEN $2
2459            THEN NULL ELSE COALESCE($3, b) END, b = CASE WHEN $4 THEN NULL ELSE
2460            COALESCE($5, b) END, s = (SELECT true FROM bar WHERE bar.foo = $99 AND bar.foo > $100),
2461            c = CASE WHEN $6 THEN NULL ELSE COALESCE($7, c) END,
2462            d = CASE WHEN $8 THEN NULL ELSE COALESCE($9, dddddddd) + bbbbb END,
2463            e = (SELECT true FROM bar) WHERE id = $1";
2464        let options = FormatOptions {
2465            max_inline_arguments: Some(60),
2466            max_inline_block: 60,
2467            max_inline_top_level: Some(60),
2468            ..Default::default()
2469        };
2470        let expected = indoc!(
2471            "
2472          UPDATE t SET
2473            o = ($5 + $6 + $7 + $8),
2474            a = CASE WHEN $2 THEN NULL ELSE COALESCE($3, b) END,
2475            b = CASE WHEN $4 THEN NULL ELSE COALESCE($5, b) END,
2476            s = (
2477              SELECT true
2478              FROM bar
2479              WHERE bar.foo = $99
2480              AND bar.foo > $100
2481            ),
2482            c = CASE WHEN $6 THEN NULL ELSE COALESCE($7, c) END,
2483            d = CASE
2484              WHEN $8 THEN NULL
2485              ELSE COALESCE($9, dddddddd) + bbbbb
2486            END,
2487            e = (SELECT true FROM bar)
2488          WHERE id = $1"
2489        );
2490
2491        assert_eq!(format(input, &QueryParams::None, &options), expected);
2492    }
2493
2494    #[test]
2495    fn parse_union_all() {
2496        let input = "SELECT id FROM a UNION ALL SELECT id FROM b WHERE c = $12 AND f";
2497        let options = FormatOptions::default();
2498        let expected = indoc!(
2499            "
2500            SELECT
2501              id
2502            FROM
2503              a
2504            UNION ALL
2505            SELECT
2506              id
2507            FROM
2508              b
2509            WHERE
2510              c = $12
2511              AND f"
2512        );
2513        assert_eq!(format(input, &QueryParams::None, &options), expected);
2514    }
2515}