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);
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}
47
48impl<'a> Default for FormatOptions<'a> {
49    fn default() -> Self {
50        FormatOptions {
51            indent: Indent::Spaces(2),
52            uppercase: None,
53            lines_between_queries: 1,
54            ignore_case_convert: None,
55        }
56    }
57}
58
59#[derive(Debug, Clone, Copy)]
60pub enum Indent {
61    Spaces(u8),
62    Tabs,
63}
64
65#[derive(Debug, Clone, Default)]
66pub enum QueryParams {
67    Named(Vec<(String, String)>),
68    Indexed(Vec<String>),
69    #[default]
70    None,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use indoc::indoc;
77
78    #[test]
79    fn it_uses_given_indent_config_for_indentation() {
80        let input = "SELECT count(*),Column1 FROM Table1;";
81        let options = FormatOptions {
82            indent: Indent::Spaces(4),
83            ..FormatOptions::default()
84        };
85        let expected = indoc!(
86            "
87            SELECT
88                count(*),
89                Column1
90            FROM
91                Table1;"
92        );
93
94        assert_eq!(format(input, &QueryParams::None, &options), expected);
95    }
96
97    #[test]
98    fn it_formats_simple_set_schema_queries() {
99        let input = "SET SCHEMA schema1; SET CURRENT SCHEMA schema2;";
100        let options = FormatOptions::default();
101        let expected = indoc!(
102            "
103            SET SCHEMA
104              schema1;
105            SET CURRENT SCHEMA
106              schema2;"
107        );
108
109        assert_eq!(format(input, &QueryParams::None, &options), expected);
110    }
111
112    #[test]
113    fn it_formats_simple_select_query() {
114        let input = "SELECT count(*),Column1 FROM Table1;";
115        let options = FormatOptions::default();
116        let expected = indoc!(
117            "
118            SELECT
119              count(*),
120              Column1
121            FROM
122              Table1;"
123        );
124
125        assert_eq!(format(input, &QueryParams::None, &options), expected);
126    }
127
128    #[test]
129    fn it_formats_complex_select() {
130        let input =
131            "SELECT DISTINCT name, ROUND(age/7) field1, 18 + 20 AS field2, 'some string' FROM foo;";
132        let options = FormatOptions::default();
133        let expected = indoc!(
134            "
135            SELECT
136              DISTINCT name,
137              ROUND(age / 7) field1,
138              18 + 20 AS field2,
139              'some string'
140            FROM
141              foo;"
142        );
143
144        assert_eq!(format(input, &QueryParams::None, &options), expected);
145    }
146
147    #[test]
148    fn it_formats_select_with_complex_where() {
149        let input = indoc!(
150            "
151            SELECT * FROM foo WHERE Column1 = 'testing'
152            AND ( (Column2 = Column3 OR Column4 >= NOW()) );
153      "
154        );
155        let options = FormatOptions::default();
156        let expected = indoc!(
157            "
158            SELECT
159              *
160            FROM
161              foo
162            WHERE
163              Column1 = 'testing'
164              AND (
165                (
166                  Column2 = Column3
167                  OR Column4 >= NOW()
168                )
169              );"
170        );
171
172        assert_eq!(format(input, &QueryParams::None, &options), expected);
173    }
174
175    #[test]
176    fn it_formats_select_with_top_level_reserved_words() {
177        let input = indoc!(
178            "
179            SELECT * FROM foo WHERE name = 'John' GROUP BY some_column
180            HAVING column > 10 ORDER BY other_column LIMIT 5;
181      "
182        );
183        let options = FormatOptions::default();
184        let expected = indoc!(
185            "
186            SELECT
187              *
188            FROM
189              foo
190            WHERE
191              name = 'John'
192            GROUP BY
193              some_column
194            HAVING
195              column > 10
196            ORDER BY
197              other_column
198            LIMIT
199              5;"
200        );
201
202        assert_eq!(format(input, &QueryParams::None, &options), expected);
203    }
204
205    #[test]
206    fn it_formats_limit_with_two_comma_separated_values_on_single_line() {
207        let input = "LIMIT 5, 10;";
208        let options = FormatOptions::default();
209        let expected = indoc!(
210            "
211            LIMIT
212              5, 10;"
213        );
214
215        assert_eq!(format(input, &QueryParams::None, &options), expected);
216    }
217
218    #[test]
219    fn it_formats_limit_of_single_value_followed_by_another_select_using_commas() {
220        let input = "LIMIT 5; SELECT foo, bar;";
221        let options = FormatOptions::default();
222        let expected = indoc!(
223            "
224            LIMIT
225              5;
226            SELECT
227              foo,
228              bar;"
229        );
230
231        assert_eq!(format(input, &QueryParams::None, &options), expected);
232    }
233
234    #[test]
235    fn it_formats_limit_of_single_value_and_offset() {
236        let input = "LIMIT 5 OFFSET 8;";
237        let options = FormatOptions::default();
238        let expected = indoc!(
239            "
240            LIMIT
241              5 OFFSET 8;"
242        );
243
244        assert_eq!(format(input, &QueryParams::None, &options), expected);
245    }
246
247    #[test]
248    fn it_recognizes_limit_in_lowercase() {
249        let input = "limit 5, 10;";
250        let options = FormatOptions::default();
251        let expected = indoc!(
252            "
253            limit
254              5, 10;"
255        );
256
257        assert_eq!(format(input, &QueryParams::None, &options), expected);
258    }
259
260    #[test]
261    fn it_preserves_case_of_keywords() {
262        let input = "select distinct * frOM foo left join bar WHERe a > 1 and b = 3";
263        let options = FormatOptions::default();
264        let expected = indoc!(
265            "
266            select
267              distinct *
268            frOM
269              foo
270              left join bar
271            WHERe
272              a > 1
273              and b = 3"
274        );
275
276        assert_eq!(format(input, &QueryParams::None, &options), expected);
277    }
278
279    #[test]
280    fn it_formats_select_query_with_select_query_inside_it() {
281        let input = "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b";
282        let options = FormatOptions::default();
283        let expected = indoc!(
284            "
285            SELECT
286              *,
287              SUM(*) AS sum
288            FROM
289              (
290                SELECT
291                  *
292                FROM
293                  Posts
294                LIMIT
295                  30
296              )
297            WHERE
298              a > b"
299        );
300
301        assert_eq!(format(input, &QueryParams::None, &options), expected);
302    }
303
304    #[test]
305    fn it_formats_select_query_with_inner_join() {
306        let input = indoc!(
307            "
308            SELECT customer_id.from, COUNT(order_id) AS total FROM customers
309            INNER JOIN orders ON customers.customer_id = orders.customer_id;"
310        );
311        let options = FormatOptions::default();
312        let expected = indoc!(
313            "
314            SELECT
315              customer_id.from,
316              COUNT(order_id) AS total
317            FROM
318              customers
319              INNER JOIN orders ON customers.customer_id = orders.customer_id;"
320        );
321
322        assert_eq!(format(input, &QueryParams::None, &options), expected);
323    }
324
325    #[test]
326    fn it_formats_select_query_with_non_standard_join() {
327        let input = indoc!(
328            "
329            SELECT customer_id.from, COUNT(order_id) AS total FROM customers
330            INNER ANY JOIN orders ON customers.customer_id = orders.customer_id
331            LEFT
332            SEMI JOIN foo ON foo.id = customers.id
333            PASTE
334            JOIN bar
335            ;"
336        );
337        let options = FormatOptions::default();
338        let expected = indoc!(
339            "
340            SELECT
341              customer_id.from,
342              COUNT(order_id) AS total
343            FROM
344              customers
345              INNER ANY JOIN orders ON customers.customer_id = orders.customer_id
346              LEFT SEMI JOIN foo ON foo.id = customers.id
347              PASTE JOIN bar;"
348        );
349
350        assert_eq!(format(input, &QueryParams::None, &options), expected);
351    }
352
353    #[test]
354    fn it_formats_select_query_with_different_comments() {
355        let input = indoc!(
356            "
357            SELECT
358            /*
359             * This is a block comment
360             */
361            * FROM
362            -- This is another comment
363            MyTable # One final comment
364            WHERE 1 = 2;"
365        );
366        let options = FormatOptions::default();
367        let expected = indoc!(
368            "
369            SELECT
370              /*
371               * This is a block comment
372               */
373              *
374            FROM
375              -- This is another comment
376              MyTable # One final comment
377            WHERE
378              1 = 2;"
379        );
380
381        assert_eq!(format(input, &QueryParams::None, &options), expected);
382    }
383
384    #[test]
385    fn it_maintains_block_comment_indentation() {
386        let input = indoc!(
387            "
388            SELECT
389              /*
390               * This is a block comment
391               */
392              *
393            FROM
394              MyTable
395            WHERE
396              1 = 2;"
397        );
398        let options = FormatOptions::default();
399
400        assert_eq!(format(input, &QueryParams::None, &options), input);
401    }
402
403    #[test]
404    fn it_formats_simple_insert_query() {
405        let input = "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');";
406        let options = FormatOptions::default();
407        let expected = indoc!(
408            "
409            INSERT INTO
410              Customers (ID, MoneyBalance, Address, City)
411            VALUES
412              (12, -123.4, 'Skagen 2111', 'Stv');"
413        );
414
415        assert_eq!(format(input, &QueryParams::None, &options), expected);
416    }
417
418    #[test]
419    fn it_keeps_short_parenthesized_list_with_nested_parenthesis_on_single_line() {
420        let input = "SELECT (a + b * (c - NOW()));";
421        let options = FormatOptions::default();
422        let expected = indoc!(
423            "
424            SELECT
425              (a + b * (c - NOW()));"
426        );
427
428        assert_eq!(format(input, &QueryParams::None, &options), expected);
429    }
430
431    #[test]
432    fn it_breaks_long_parenthesized_lists_to_multiple_lines() {
433        let input = indoc!(
434            "
435            INSERT INTO some_table (id_product, id_shop, id_currency, id_country, id_registration) (
436            SELECT IF(dq.id_discounter_shopping = 2, dq.value, dq.value / 100),
437            IF (dq.id_discounter_shopping = 2, 'amount', 'percentage') FROM foo);"
438        );
439        let options = FormatOptions::default();
440        let expected = indoc!(
441            "
442            INSERT INTO
443              some_table (
444                id_product,
445                id_shop,
446                id_currency,
447                id_country,
448                id_registration
449              ) (
450                SELECT
451                  IF(
452                    dq.id_discounter_shopping = 2,
453                    dq.value,
454                    dq.value / 100
455                  ),
456                  IF (
457                    dq.id_discounter_shopping = 2,
458                    'amount',
459                    'percentage'
460                  )
461                FROM
462                  foo
463              );"
464        );
465
466        assert_eq!(format(input, &QueryParams::None, &options), expected);
467    }
468
469    #[test]
470    fn it_formats_simple_update_query() {
471        let input = "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';";
472        let options = FormatOptions::default();
473        let expected = indoc!(
474            "
475            UPDATE
476              Customers
477            SET
478              ContactName = 'Alfred Schmidt',
479              City = 'Hamburg'
480            WHERE
481              CustomerName = 'Alfreds Futterkiste';"
482        );
483
484        assert_eq!(format(input, &QueryParams::None, &options), expected);
485    }
486
487    #[test]
488    fn it_formats_simple_delete_query() {
489        let input = "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;";
490        let options = FormatOptions::default();
491        let expected = indoc!(
492            "
493            DELETE FROM
494              Customers
495            WHERE
496              CustomerName = 'Alfred'
497              AND Phone = 5002132;"
498        );
499
500        assert_eq!(format(input, &QueryParams::None, &options), expected);
501    }
502
503    #[test]
504    fn it_formats_simple_drop_query() {
505        let input = "DROP TABLE IF EXISTS admin_role;";
506        let options = FormatOptions::default();
507
508        assert_eq!(format(input, &QueryParams::None, &options), input);
509    }
510
511    #[test]
512    fn it_formats_incomplete_query() {
513        let input = "SELECT count(";
514        let options = FormatOptions::default();
515        let expected = indoc!(
516            "
517            SELECT
518              count("
519        );
520
521        assert_eq!(format(input, &QueryParams::None, &options), expected);
522    }
523
524    #[test]
525    fn it_formats_query_that_ends_with_open_comment() {
526        let input = indoc!(
527            "
528            SELECT count(*)
529            /*Comment"
530        );
531        let options = FormatOptions::default();
532        let expected = indoc!(
533            "
534            SELECT
535              count(*)
536              /*Comment"
537        );
538
539        assert_eq!(format(input, &QueryParams::None, &options), expected);
540    }
541
542    #[test]
543    fn it_formats_update_query_with_as_part() {
544        let input = "UPDATE customers SET total_orders = order_summary.total  FROM ( SELECT * FROM bank) AS order_summary";
545        let options = FormatOptions::default();
546        let expected = indoc!(
547            "
548            UPDATE
549              customers
550            SET
551              total_orders = order_summary.total
552            FROM
553              (
554                SELECT
555                  *
556                FROM
557                  bank
558              ) AS order_summary"
559        );
560
561        assert_eq!(format(input, &QueryParams::None, &options), expected);
562    }
563
564    #[test]
565    fn it_formats_top_level_and_newline_multi_word_reserved_words_with_inconsistent_spacing() {
566        let input = "SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah";
567        let options = FormatOptions::default();
568        let expected = indoc!(
569            "
570            SELECT
571              *
572            FROM
573              foo
574              LEFT OUTER JOIN bar
575            ORDER BY
576              blah"
577        );
578
579        assert_eq!(format(input, &QueryParams::None, &options), expected);
580    }
581
582    #[test]
583    fn it_formats_long_double_parenthesized_queries_to_multiple_lines() {
584        let input = "((foo = '0123456789-0123456789-0123456789-0123456789'))";
585        let options = FormatOptions::default();
586        let expected = indoc!(
587            "
588            (
589              (
590                foo = '0123456789-0123456789-0123456789-0123456789'
591              )
592            )"
593        );
594
595        assert_eq!(format(input, &QueryParams::None, &options), expected);
596    }
597
598    #[test]
599    fn it_formats_short_double_parenthesizes_queries_to_one_line() {
600        let input = "((foo = 'bar'))";
601        let options = FormatOptions::default();
602
603        assert_eq!(format(input, &QueryParams::None, &options), input);
604    }
605
606    #[test]
607    fn it_formats_single_char_operators() {
608        let inputs = [
609            "foo = bar",
610            "foo < bar",
611            "foo > bar",
612            "foo + bar",
613            "foo - bar",
614            "foo * bar",
615            "foo / bar",
616            "foo % bar",
617        ];
618        let options = FormatOptions::default();
619        for input in &inputs {
620            assert_eq!(&format(input, &QueryParams::None, &options), input);
621        }
622    }
623
624    #[test]
625    fn it_formats_multi_char_operators() {
626        let inputs = [
627            "foo != bar",
628            "foo <> bar",
629            "foo == bar",
630            "foo || bar",
631            "foo <= bar",
632            "foo >= bar",
633            "foo !< bar",
634            "foo !> bar",
635        ];
636        let options = FormatOptions::default();
637        for input in &inputs {
638            assert_eq!(&format(input, &QueryParams::None, &options), input);
639        }
640    }
641
642    #[test]
643    fn it_formats_logical_operators() {
644        let inputs = [
645            "foo ALL bar",
646            "foo = ANY (1, 2, 3)",
647            "EXISTS bar",
648            "foo IN (1, 2, 3)",
649            "foo LIKE 'hello%'",
650            "foo IS NULL",
651            "UNIQUE foo",
652        ];
653        let options = FormatOptions::default();
654        for input in &inputs {
655            assert_eq!(&format(input, &QueryParams::None, &options), input);
656        }
657    }
658
659    #[test]
660    fn it_formats_and_or_operators() {
661        let strings = [
662            ("foo BETWEEN bar AND baz", "foo BETWEEN bar AND baz"),
663            ("foo BETWEEN\nbar\nAND baz", "foo BETWEEN bar AND baz"),
664            ("foo AND bar", "foo\nAND bar"),
665            ("foo OR bar", "foo\nOR bar"),
666        ];
667        let options = FormatOptions::default();
668        for (input, output) in &strings {
669            assert_eq!(&format(input, &QueryParams::None, &options), output);
670        }
671    }
672
673    #[test]
674    fn it_recognizes_strings() {
675        let inputs = ["\"foo JOIN bar\"", "'foo JOIN bar'", "`foo JOIN bar`"];
676        let options = FormatOptions::default();
677        for input in &inputs {
678            assert_eq!(&format(input, &QueryParams::None, &options), input);
679        }
680    }
681
682    #[test]
683    fn it_recognizes_escaped_strings() {
684        let inputs = [
685            r#""foo \" JOIN bar""#,
686            r#"'foo \' JOIN bar'"#,
687            r#"`foo `` JOIN bar`"#,
688            r#"'foo '' JOIN bar'"#,
689            r#"'two households"'"#,
690            r#"'two households'''"#,
691            r#"E'alice'''"#,
692        ];
693        let options = FormatOptions::default();
694        for input in &inputs {
695            assert_eq!(&format(input, &QueryParams::None, &options), input);
696        }
697    }
698
699    #[test]
700    fn it_formats_postgres_specific_operators() {
701        let strings = [
702            ("column::int", "column::int"),
703            ("v->2", "v -> 2"),
704            ("v->>2", "v ->> 2"),
705            ("foo ~~ 'hello'", "foo ~~ 'hello'"),
706            ("foo !~ 'hello'", "foo !~ 'hello'"),
707            ("foo ~* 'hello'", "foo ~* 'hello'"),
708            ("foo ~~* 'hello'", "foo ~~* 'hello'"),
709            ("foo !~~ 'hello'", "foo !~~ 'hello'"),
710            ("foo !~* 'hello'", "foo !~* 'hello'"),
711            ("foo !~~* 'hello'", "foo !~~* 'hello'"),
712        ];
713        let options = FormatOptions::default();
714        for (input, output) in &strings {
715            assert_eq!(&format(input, &QueryParams::None, &options), output);
716        }
717    }
718
719    #[test]
720    fn it_keeps_separation_between_multiple_statements() {
721        let strings = [
722            ("foo;bar;", "foo;\nbar;"),
723            ("foo\n;bar;", "foo;\nbar;"),
724            ("foo\n\n\n;bar;\n\n", "foo;\nbar;"),
725        ];
726        let options = FormatOptions::default();
727        for (input, output) in &strings {
728            assert_eq!(&format(input, &QueryParams::None, &options), output);
729        }
730
731        let input = indoc!(
732            "
733            SELECT count(*),Column1 FROM Table1;
734            SELECT count(*),Column1 FROM Table2;"
735        );
736        let options = FormatOptions::default();
737        let expected = indoc!(
738            "
739            SELECT
740              count(*),
741              Column1
742            FROM
743              Table1;
744            SELECT
745              count(*),
746              Column1
747            FROM
748              Table2;"
749        );
750
751        assert_eq!(format(input, &QueryParams::None, &options), expected);
752    }
753
754    #[test]
755    fn it_formats_unicode_correctly() {
756        let input = "SELECT test, тест FROM table;";
757        let options = FormatOptions::default();
758        let expected = indoc!(
759            "
760            SELECT
761              test,
762              тест
763            FROM
764              table;"
765        );
766
767        assert_eq!(format(input, &QueryParams::None, &options), expected);
768    }
769
770    #[test]
771    fn it_converts_keywords_to_uppercase_when_option_passed_in() {
772        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
773        let options = FormatOptions {
774            uppercase: Some(true),
775            ..FormatOptions::default()
776        };
777        let expected = indoc!(
778            "
779            SELECT
780              DISTINCT *
781            FROM
782              foo
783              LEFT JOIN bar
784            WHERE
785              cola > 1
786              AND colb = 3"
787        );
788
789        assert_eq!(format(input, &QueryParams::None, &options), expected);
790    }
791
792    #[test]
793    fn it_line_breaks_between_queries_with_config() {
794        let input = "SELECT * FROM foo; SELECT * FROM bar;";
795        let options = FormatOptions {
796            lines_between_queries: 2,
797            ..FormatOptions::default()
798        };
799        let expected = indoc!(
800            "
801            SELECT
802              *
803            FROM
804              foo;
805
806            SELECT
807              *
808            FROM
809              bar;"
810        );
811
812        assert_eq!(format(input, &QueryParams::None, &options), expected);
813    }
814
815    #[test]
816    fn it_correctly_indents_create_statement_after_select() {
817        let input = indoc!(
818            "
819            SELECT * FROM test;
820            CREATE TABLE TEST(id NUMBER NOT NULL, col1 VARCHAR2(20), col2 VARCHAR2(20));
821        "
822        );
823        let options = FormatOptions::default();
824        let expected = indoc!(
825            "
826            SELECT
827              *
828            FROM
829              test;
830            CREATE TABLE TEST(
831              id NUMBER NOT NULL,
832              col1 VARCHAR2(20),
833              col2 VARCHAR2(20)
834            );"
835        );
836
837        assert_eq!(format(input, &QueryParams::None, &options), expected);
838    }
839
840    #[test]
841    fn it_formats_short_create_table() {
842        let input = "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);";
843        let options = FormatOptions::default();
844
845        assert_eq!(format(input, &QueryParams::None, &options), input);
846    }
847
848    #[test]
849    fn it_formats_long_create_table() {
850        let input =
851            "CREATE TABLE items (a INT PRIMARY KEY, b TEXT, c INT NOT NULL, d INT NOT NULL);";
852        let options = FormatOptions::default();
853        let expected = indoc!(
854            "
855            CREATE TABLE items (
856              a INT PRIMARY KEY,
857              b TEXT,
858              c INT NOT NULL,
859              d INT NOT NULL
860            );"
861        );
862
863        assert_eq!(format(input, &QueryParams::None, &options), expected);
864    }
865
866    #[test]
867    fn it_formats_insert_without_into() {
868        let input =
869            "INSERT Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');";
870        let options = FormatOptions::default();
871        let expected = indoc!(
872            "
873            INSERT
874              Customers (ID, MoneyBalance, Address, City)
875            VALUES
876              (12, -123.4, 'Skagen 2111', 'Stv');"
877        );
878
879        assert_eq!(format(input, &QueryParams::None, &options), expected);
880    }
881
882    #[test]
883    fn it_formats_alter_table_modify_query() {
884        let input = "ALTER TABLE supplier MODIFY supplier_name char(100) NOT NULL;";
885        let options = FormatOptions::default();
886        let expected = indoc!(
887            "
888            ALTER TABLE
889              supplier
890            MODIFY
891              supplier_name char(100) NOT NULL;"
892        );
893
894        assert_eq!(format(input, &QueryParams::None, &options), expected);
895    }
896
897    #[test]
898    fn it_formats_alter_table_alter_column_query() {
899        let input = "ALTER TABLE supplier ALTER COLUMN supplier_name VARCHAR(100) NOT NULL;";
900        let options = FormatOptions::default();
901        let expected = indoc!(
902            "
903            ALTER TABLE
904              supplier
905            ALTER COLUMN
906              supplier_name VARCHAR(100) NOT NULL;"
907        );
908
909        assert_eq!(format(input, &QueryParams::None, &options), expected);
910    }
911
912    #[test]
913    fn it_recognizes_bracketed_strings() {
914        let inputs = ["[foo JOIN bar]", "[foo ]] JOIN bar]"];
915        let options = FormatOptions::default();
916        for input in &inputs {
917            assert_eq!(&format(input, &QueryParams::None, &options), input);
918        }
919    }
920
921    #[test]
922    fn it_recognizes_at_variables() {
923        let input =
924            "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name];";
925        let options = FormatOptions::default();
926        let expected = indoc!(
927            "
928            SELECT
929              @variable,
930              @a1_2.3$,
931              @'var name',
932              @\"var name\",
933              @`var name`,
934              @[var name];"
935        );
936
937        assert_eq!(format(input, &QueryParams::None, &options), expected);
938    }
939
940    #[test]
941    fn it_recognizes_at_variables_with_param_values() {
942        let input =
943            "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name], @'var\\name';";
944        let params = vec![
945            ("variable".to_string(), "\"variable value\"".to_string()),
946            ("a1_2.3$".to_string(), "'weird value'".to_string()),
947            ("var name".to_string(), "'var value'".to_string()),
948            ("var\\name".to_string(), "'var\\ value'".to_string()),
949        ];
950        let options = FormatOptions::default();
951        let expected = indoc!(
952            "
953            SELECT
954              \"variable value\",
955              'weird value',
956              'var value',
957              'var value',
958              'var value',
959              'var value',
960              'var\\ value';"
961        );
962
963        assert_eq!(
964            format(input, &QueryParams::Named(params), &options),
965            expected
966        );
967    }
968
969    #[test]
970    fn it_recognizes_colon_variables() {
971        let input =
972            "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`, :[var name];";
973        let options = FormatOptions::default();
974        let expected = indoc!(
975            "
976            SELECT
977              :variable,
978              :a1_2.3$,
979              :'var name',
980              :\"var name\",
981              :`var name`,
982              :[var name];"
983        );
984
985        assert_eq!(format(input, &QueryParams::None, &options), expected);
986    }
987
988    #[test]
989    fn it_recognizes_colon_variables_with_param_values() {
990        let input = indoc!(
991            "
992            SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`,
993            :[var name], :'escaped \\'var\\'', :\"^*& weird \\\" var   \";
994            "
995        );
996        let params = vec![
997            ("variable".to_string(), "\"variable value\"".to_string()),
998            ("a1_2.3$".to_string(), "'weird value'".to_string()),
999            ("var name".to_string(), "'var value'".to_string()),
1000            ("escaped 'var'".to_string(), "'weirder value'".to_string()),
1001            (
1002                "^*& weird \" var   ".to_string(),
1003                "'super weird value'".to_string(),
1004            ),
1005        ];
1006        let options = FormatOptions::default();
1007        let expected = indoc!(
1008            "
1009            SELECT
1010              \"variable value\",
1011              'weird value',
1012              'var value',
1013              'var value',
1014              'var value',
1015              'var value',
1016              'weirder value',
1017              'super weird value';"
1018        );
1019
1020        assert_eq!(
1021            format(input, &QueryParams::Named(params), &options),
1022            expected
1023        );
1024    }
1025
1026    #[test]
1027    fn it_recognizes_question_numbered_placeholders() {
1028        let input = "SELECT ?1, ?25, ?;";
1029        let options = FormatOptions::default();
1030        let expected = indoc!(
1031            "
1032            SELECT
1033              ?1,
1034              ?25,
1035              ?;"
1036        );
1037
1038        assert_eq!(format(input, &QueryParams::None, &options), expected);
1039    }
1040
1041    #[test]
1042    fn it_recognizes_question_numbered_placeholders_with_param_values() {
1043        let input = "SELECT ?1, ?2, ?0;";
1044        let params = vec![
1045            "first".to_string(),
1046            "second".to_string(),
1047            "third".to_string(),
1048        ];
1049        let options = FormatOptions::default();
1050        let expected = indoc!(
1051            "
1052            SELECT
1053              second,
1054              third,
1055              first;"
1056        );
1057
1058        assert_eq!(
1059            format(input, &QueryParams::Indexed(params), &options),
1060            expected
1061        );
1062
1063        format("?62666666121266666612", &QueryParams::None, &options);
1064    }
1065
1066    #[test]
1067    fn it_recognizes_question_indexed_placeholders_with_param_values() {
1068        let input = "SELECT ?, ?, ?;";
1069        let params = vec![
1070            "first".to_string(),
1071            "second".to_string(),
1072            "third".to_string(),
1073        ];
1074        let options = FormatOptions::default();
1075        let expected = indoc!(
1076            "
1077            SELECT
1078              first,
1079              second,
1080              third;"
1081        );
1082
1083        assert_eq!(
1084            format(input, &QueryParams::Indexed(params), &options),
1085            expected
1086        );
1087    }
1088
1089    #[test]
1090    fn it_recognizes_dollar_sign_numbered_placeholders() {
1091        let input = "SELECT $1, $2;";
1092        let options = FormatOptions::default();
1093        let expected = indoc!(
1094            "
1095            SELECT
1096              $1,
1097              $2;"
1098        );
1099
1100        assert_eq!(format(input, &QueryParams::None, &options), expected);
1101    }
1102
1103    #[test]
1104    fn it_recognizes_dollar_sign_alphanumeric_placeholders() {
1105        let input = "SELECT $hash, $foo, $bar;";
1106        let options = FormatOptions::default();
1107        let expected = indoc!(
1108            "
1109            SELECT
1110              $hash,
1111              $foo,
1112              $bar;"
1113        );
1114
1115        assert_eq!(format(input, &QueryParams::None, &options), expected);
1116    }
1117
1118    #[test]
1119    fn it_recognizes_dollar_sign_numbered_placeholders_with_param_values() {
1120        let input = "SELECT $2, $3, $1, $named, $4, $alias;";
1121        let params = vec![
1122            "first".to_string(),
1123            "second".to_string(),
1124            "third".to_string(),
1125            "4th".to_string(),
1126        ];
1127        let options = FormatOptions::default();
1128        let expected = indoc!(
1129            "
1130            SELECT
1131              second,
1132              third,
1133              first,
1134              $named,
1135              4th,
1136              $alias;"
1137        );
1138
1139        assert_eq!(
1140            format(input, &QueryParams::Indexed(params), &options),
1141            expected
1142        );
1143    }
1144
1145    #[test]
1146    fn it_recognizes_dollar_sign_alphanumeric_placeholders_with_param_values() {
1147        let input = "SELECT $hash, $salt, $1, $2;";
1148        let params = vec![
1149            ("hash".to_string(), "hash value".to_string()),
1150            ("salt".to_string(), "salt value".to_string()),
1151            ("1".to_string(), "number 1".to_string()),
1152            ("2".to_string(), "number 2".to_string()),
1153        ];
1154        let options = FormatOptions::default();
1155        let expected = indoc!(
1156            "
1157            SELECT
1158              hash value,
1159              salt value,
1160              number 1,
1161              number 2;"
1162        );
1163
1164        assert_eq!(
1165            format(input, &QueryParams::Named(params), &options),
1166            expected
1167        );
1168    }
1169
1170    #[test]
1171    fn it_formats_query_with_go_batch_separator() {
1172        let input = "SELECT 1 GO SELECT 2";
1173        let params = vec![
1174            "first".to_string(),
1175            "second".to_string(),
1176            "third".to_string(),
1177        ];
1178        let options = FormatOptions::default();
1179        let expected = indoc!(
1180            "
1181            SELECT
1182              1
1183            GO
1184            SELECT
1185              2"
1186        );
1187
1188        assert_eq!(
1189            format(input, &QueryParams::Indexed(params), &options),
1190            expected
1191        );
1192    }
1193
1194    #[test]
1195    fn it_formats_select_query_with_cross_join() {
1196        let input = "SELECT a, b FROM t CROSS JOIN t2 on t.id = t2.id_t";
1197        let options = FormatOptions::default();
1198        let expected = indoc!(
1199            "
1200            SELECT
1201              a,
1202              b
1203            FROM
1204              t
1205              CROSS JOIN t2 on t.id = t2.id_t"
1206        );
1207
1208        assert_eq!(format(input, &QueryParams::None, &options), expected);
1209    }
1210
1211    #[test]
1212    fn it_formats_select_query_with_cross_apply() {
1213        let input = "SELECT a, b FROM t CROSS APPLY fn(t.id)";
1214        let options = FormatOptions::default();
1215        let expected = indoc!(
1216            "
1217            SELECT
1218              a,
1219              b
1220            FROM
1221              t
1222              CROSS APPLY fn(t.id)"
1223        );
1224
1225        assert_eq!(format(input, &QueryParams::None, &options), expected);
1226    }
1227
1228    #[test]
1229    fn it_formats_simple_select() {
1230        let input = "SELECT N, M FROM t";
1231        let options = FormatOptions::default();
1232        let expected = indoc!(
1233            "
1234            SELECT
1235              N,
1236              M
1237            FROM
1238              t"
1239        );
1240
1241        assert_eq!(format(input, &QueryParams::None, &options), expected);
1242    }
1243
1244    #[test]
1245    fn it_formats_simple_select_with_national_characters_mssql() {
1246        let input = "SELECT N'value'";
1247        let options = FormatOptions::default();
1248        let expected = indoc!(
1249            "
1250            SELECT
1251              N'value'"
1252        );
1253
1254        assert_eq!(format(input, &QueryParams::None, &options), expected);
1255    }
1256
1257    #[test]
1258    fn it_formats_select_query_with_outer_apply() {
1259        let input = "SELECT a, b FROM t OUTER APPLY fn(t.id)";
1260        let options = FormatOptions::default();
1261        let expected = indoc!(
1262            "
1263            SELECT
1264              a,
1265              b
1266            FROM
1267              t
1268              OUTER APPLY fn(t.id)"
1269        );
1270
1271        assert_eq!(format(input, &QueryParams::None, &options), expected);
1272    }
1273
1274    #[test]
1275    fn it_formats_fetch_first_like_limit() {
1276        let input = "SELECT * FETCH FIRST 2 ROWS ONLY;";
1277        let options = FormatOptions::default();
1278        let expected = indoc!(
1279            "
1280            SELECT
1281              *
1282            FETCH FIRST
1283              2 ROWS ONLY;"
1284        );
1285
1286        assert_eq!(format(input, &QueryParams::None, &options), expected);
1287    }
1288
1289    #[test]
1290    fn it_formats_case_when_with_a_blank_expression() {
1291        let input = "CASE WHEN option = 'foo' THEN 1 WHEN option = 'bar' THEN 2 WHEN option = 'baz' THEN 3 ELSE 4 END;";
1292        let options = FormatOptions::default();
1293        let expected = indoc!(
1294            "
1295            CASE
1296              WHEN option = 'foo' THEN 1
1297              WHEN option = 'bar' THEN 2
1298              WHEN option = 'baz' THEN 3
1299              ELSE 4
1300            END;"
1301        );
1302
1303        assert_eq!(format(input, &QueryParams::None, &options), expected);
1304    }
1305
1306    #[test]
1307    fn it_formats_case_when_inside_select() {
1308        let input =
1309            "SELECT foo, bar, CASE baz WHEN 'one' THEN 1 WHEN 'two' THEN 2 ELSE 3 END FROM table";
1310        let options = FormatOptions::default();
1311        let expected = indoc!(
1312            "
1313            SELECT
1314              foo,
1315              bar,
1316              CASE
1317                baz
1318                WHEN 'one' THEN 1
1319                WHEN 'two' THEN 2
1320                ELSE 3
1321              END
1322            FROM
1323              table"
1324        );
1325
1326        assert_eq!(format(input, &QueryParams::None, &options), expected);
1327    }
1328
1329    #[test]
1330    fn it_formats_case_when_with_an_expression() {
1331        let input = "CASE toString(getNumber()) WHEN 'one' THEN 1 WHEN 'two' THEN 2 WHEN 'three' THEN 3 ELSE 4 END;";
1332        let options = FormatOptions::default();
1333        let expected = indoc!(
1334            "
1335            CASE
1336              toString(getNumber())
1337              WHEN 'one' THEN 1
1338              WHEN 'two' THEN 2
1339              WHEN 'three' THEN 3
1340              ELSE 4
1341            END;"
1342        );
1343
1344        assert_eq!(format(input, &QueryParams::None, &options), expected);
1345    }
1346
1347    #[test]
1348    fn it_recognizes_lowercase_case_end() {
1349        let input = "case when option = 'foo' then 1 else 2 end;";
1350        let options = FormatOptions::default();
1351        let expected = indoc!(
1352            "
1353            case
1354              when option = 'foo' then 1
1355              else 2
1356            end;"
1357        );
1358
1359        assert_eq!(format(input, &QueryParams::None, &options), expected);
1360    }
1361
1362    #[test]
1363    fn it_ignores_words_case_and_end_inside_other_strings() {
1364        let input = "SELECT CASEDATE, ENDDATE FROM table1;";
1365        let options = FormatOptions::default();
1366        let expected = indoc!(
1367            "
1368            SELECT
1369              CASEDATE,
1370              ENDDATE
1371            FROM
1372              table1;"
1373        );
1374
1375        assert_eq!(format(input, &QueryParams::None, &options), expected);
1376    }
1377
1378    #[test]
1379    fn it_formats_tricky_line_comments() {
1380        let input = "SELECT a#comment, here\nFROM b--comment";
1381        let options = FormatOptions::default();
1382        let expected = indoc!(
1383            "
1384            SELECT
1385              a #comment, here
1386            FROM
1387              b --comment"
1388        );
1389
1390        assert_eq!(format(input, &QueryParams::None, &options), expected);
1391    }
1392
1393    #[test]
1394    fn it_formats_line_comments_followed_by_semicolon() {
1395        let input = indoc!(
1396            "
1397            SELECT a FROM b
1398            --comment
1399            ;"
1400        );
1401        let options = FormatOptions::default();
1402        let expected = indoc!(
1403            "
1404            SELECT
1405              a
1406            FROM
1407              b --comment
1408            ;"
1409        );
1410
1411        assert_eq!(format(input, &QueryParams::None, &options), expected);
1412    }
1413
1414    #[test]
1415    fn it_formats_line_comments_followed_by_comma() {
1416        let input = indoc!(
1417            "
1418            SELECT a --comment
1419            , b"
1420        );
1421        let options = FormatOptions::default();
1422        let expected = indoc!(
1423            "
1424            SELECT
1425              a --comment
1426            ,
1427              b"
1428        );
1429
1430        assert_eq!(format(input, &QueryParams::None, &options), expected);
1431    }
1432
1433    #[test]
1434    fn it_formats_line_comments_followed_by_close_paren() {
1435        let input = "SELECT ( a --comment\n )";
1436        let options = FormatOptions::default();
1437        let expected = indoc!(
1438            "
1439            SELECT
1440              (
1441                a --comment
1442              )"
1443        );
1444
1445        assert_eq!(format(input, &QueryParams::None, &options), expected);
1446    }
1447
1448    #[test]
1449    fn it_formats_line_comments_followed_by_open_paren() {
1450        let input = "SELECT a --comment\n()";
1451        let options = FormatOptions::default();
1452        let expected = indoc!(
1453            "
1454            SELECT
1455              a --comment
1456              ()"
1457        );
1458
1459        assert_eq!(format(input, &QueryParams::None, &options), expected);
1460    }
1461
1462    #[test]
1463    fn it_formats_lonely_semicolon() {
1464        let input = ";";
1465        let options = FormatOptions::default();
1466
1467        assert_eq!(format(input, &QueryParams::None, &options), input);
1468    }
1469
1470    #[test]
1471    fn it_formats_multibyte_chars() {
1472        let input = "\nSELECT 'главная'";
1473        let options = FormatOptions::default();
1474        let expected = "SELECT\n  'главная'";
1475
1476        assert_eq!(format(input, &QueryParams::None, &options), expected);
1477    }
1478
1479    #[test]
1480    fn it_recognizes_scientific_notation() {
1481        let input = "SELECT *, 1e-7 as small, 1e2 as medium, 1e+7 as large FROM t";
1482        let options = FormatOptions::default();
1483        let expected = indoc!(
1484            "
1485            SELECT
1486              *,
1487              1e-7 as small,
1488              1e2 as medium,
1489              1e+7 as large
1490            FROM
1491              t"
1492        );
1493
1494        assert_eq!(format(input, &QueryParams::None, &options), expected);
1495    }
1496
1497    #[test]
1498    fn it_keeps_double_dollar_signs_together() {
1499        let input = "CREATE FUNCTION abc() AS $$ SELECT * FROM table $$ LANGUAGE plpgsql;";
1500        let options = FormatOptions::default();
1501        let expected = indoc!(
1502            "
1503            CREATE FUNCTION abc() AS
1504            $$
1505            SELECT
1506              *
1507            FROM
1508              table
1509            $$
1510            LANGUAGE plpgsql;"
1511        );
1512
1513        assert_eq!(format(input, &QueryParams::None, &options), expected);
1514    }
1515
1516    #[test]
1517    fn it_formats_pgplsql() {
1518        let input = "CREATE FUNCTION abc() AS $$ DECLARE a int := 1; b int := 2; BEGIN SELECT * FROM table $$ LANGUAGE plpgsql;";
1519        let options = FormatOptions::default();
1520        let expected = indoc!(
1521            "
1522            CREATE FUNCTION abc() AS
1523            $$
1524            DECLARE
1525            a int := 1;
1526            b int := 2;
1527            BEGIN
1528            SELECT
1529              *
1530            FROM
1531              table
1532            $$
1533            LANGUAGE plpgsql;"
1534        );
1535
1536        assert_eq!(format(input, &QueryParams::None, &options), expected);
1537    }
1538
1539    #[test]
1540    fn it_handles_comments_correctly() {
1541        let input = indoc!(
1542            "
1543                -- 创建一个外部表,存储销售数据
1544            CREATE EXTERNAL TABLE IF NOT EXISTS sales_data (
1545                -- 唯一标识订单ID
1546                order_id BIGINT COMMENT 'Unique identifier for the order',
1547
1548                -- 客户ID
1549                customer_id BIGINT COMMENT 'Unique identifier for the customer',
1550            )
1551            COMMENT 'Sales data table for storing transaction records';
1552
1553            -- 按销售日期和城市进行分区
1554            PARTITIONED BY (
1555                sale_year STRING COMMENT 'Year of the sale',
1556                sale_month STRING COMMENT 'Month of the sale'
1557            )
1558
1559            -- 设置数据存储位置
1560            LOCATION '/user/hive/warehouse/sales_data'
1561
1562            -- 使用 ORC 存储格式
1563            STORED AS ORC
1564
1565            -- 设置表的行格式
1566            ROW FORMAT DELIMITED
1567            FIELDS TERMINATED BY ','
1568            LINES TERMINATED BY '\n'
1569
1570            -- 设置表属性
1571            TBLPROPERTIES (
1572                'orc.compress' = 'SNAPPY',          -- 使用SNAPPY压缩
1573                'transactional' = 'true',           -- 启用事务支持
1574                'orc.create.index' = 'true',        -- 创建索引
1575                'skip.header.line.count' = '1',     -- 跳过CSV文件的第一行
1576                'external.table.purge' = 'true'     -- 在删除表时自动清理数据
1577            );
1578
1579            -- 自动加载数据到 Hive 分区中
1580            ALTER TABLE sales_data
1581            ADD PARTITION (sale_year = '2024', sale_month = '08')
1582            LOCATION '/user/hive/warehouse/sales_data/2024/08';"
1583        );
1584        let options = FormatOptions {
1585            indent: Indent::Spaces(4),
1586            ..Default::default()
1587        };
1588        let expected = indoc!(
1589            "
1590            -- 创建一个外部表,存储销售数据
1591            CREATE EXTERNAL TABLE IF NOT EXISTS sales_data (
1592                -- 唯一标识订单ID
1593                order_id BIGINT COMMENT 'Unique identifier for the order',
1594                -- 客户ID
1595                customer_id BIGINT COMMENT 'Unique identifier for the customer',
1596            ) COMMENT 'Sales data table for storing transaction records';
1597            -- 按销售日期和城市进行分区
1598            PARTITIONED BY (
1599                sale_year STRING COMMENT 'Year of the sale',
1600                sale_month STRING COMMENT 'Month of the sale'
1601            )
1602            -- 设置数据存储位置
1603            LOCATION '/user/hive/warehouse/sales_data'
1604            -- 使用 ORC 存储格式
1605            STORED AS ORC
1606            -- 设置表的行格式
1607            ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
1608            -- 设置表属性
1609            TBLPROPERTIES (
1610                'orc.compress' = 'SNAPPY',  -- 使用SNAPPY压缩
1611                'transactional' = 'true',  -- 启用事务支持
1612                'orc.create.index' = 'true',  -- 创建索引
1613                'skip.header.line.count' = '1',  -- 跳过CSV文件的第一行
1614                'external.table.purge' = 'true' -- 在删除表时自动清理数据
1615            );
1616            -- 自动加载数据到 Hive 分区中
1617            ALTER TABLE
1618                sales_data
1619            ADD
1620                PARTITION (sale_year = '2024', sale_month = '08') LOCATION '/user/hive/warehouse/sales_data/2024/08';"
1621        );
1622
1623        assert_eq!(format(input, &QueryParams::None, &options), expected);
1624    }
1625
1626    #[test]
1627    fn it_recognizes_returning_clause() {
1628        let input = indoc!(
1629            "
1630          INSERT INTO
1631            users (name, email)
1632          VALUES
1633            ($1, $2) RETURNING name,
1634            email"
1635        );
1636        let options = FormatOptions::default();
1637        let expected = indoc!(
1638            "
1639          INSERT INTO
1640            users (name, email)
1641          VALUES
1642            ($1, $2)
1643          RETURNING
1644            name,
1645            email"
1646        );
1647
1648        assert_eq!(format(input, &QueryParams::None, &options), expected);
1649    }
1650
1651    #[test]
1652    fn it_recognizes_on_update_clause() {
1653        let input = indoc!(
1654            "CREATE TABLE a (b integer REFERENCES c (id) ON                                     UPDATE RESTRICT, other integer);"
1655        );
1656        let options = FormatOptions::default();
1657        let expected = indoc!(
1658            "
1659          CREATE TABLE a (
1660            b integer REFERENCES c (id) ON UPDATE RESTRICT,
1661            other integer
1662          );"
1663        );
1664        assert_eq!(format(input, &QueryParams::None, &options), expected);
1665    }
1666
1667    #[test]
1668    fn it_formats_except_on_columns() {
1669        let input = indoc!(
1670            "SELECT table_0.* EXCEPT (profit),
1671                    details.* EXCEPT (item_id),
1672                    table_0.profit
1673        FROM  table_0"
1674        );
1675        let options = FormatOptions {
1676            indent: Indent::Spaces(4),
1677            ..Default::default()
1678        };
1679        let expected = indoc!(
1680            "
1681            SELECT
1682                table_0.* EXCEPT (profit),
1683                details.* EXCEPT (item_id),
1684                table_0.profit
1685            FROM
1686                table_0"
1687        );
1688
1689        assert_eq!(format(input, &QueryParams::None, &options), expected);
1690    }
1691
1692    #[test]
1693    fn it_uses_given_ignore_case_convert_config() {
1694        let input = "select count(*),Column1 from Table1;";
1695        let options = FormatOptions {
1696            uppercase: Some(true),
1697            ignore_case_convert: Some(vec!["from"]),
1698            ..FormatOptions::default()
1699        };
1700        let expected = indoc!(
1701            "
1702            SELECT
1703              count(*),
1704              Column1
1705            from
1706              Table1;"
1707        );
1708
1709        assert_eq!(format(input, &QueryParams::None, &options), expected);
1710    }
1711
1712    #[test]
1713    fn it_recognizes_fmt_off() {
1714        let input = indoc!(
1715            "SELECT              *     FROM   sometable
1716            WHERE
1717            -- comment test here
1718                 -- fmt: off
1719                first_key.second_key = 1
1720                                -- json:first_key.second_key = 1
1721                      -- fmt: on
1722                AND
1723                   -- fm1t: off
1724                first_key.second_key = 1
1725                                    --  json:first_key.second_key = 1
1726                -- fmt:on"
1727        );
1728        let options = FormatOptions {
1729            indent: Indent::Spaces(4),
1730            ..Default::default()
1731        };
1732        let expected = indoc!(
1733            "
1734            SELECT
1735                *
1736            FROM
1737                sometable
1738            WHERE
1739                -- comment test here
1740                first_key.second_key = 1
1741                                -- json:first_key.second_key = 1
1742                AND
1743                -- fm1t: off
1744                first_key.second_key = 1
1745                --  json:first_key.second_key = 1"
1746        );
1747
1748        assert_eq!(format(input, &QueryParams::None, &options), expected);
1749    }
1750
1751    #[test]
1752    fn it_converts_keywords_to_lowercase_when_option_passed_in() {
1753        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
1754        let options = FormatOptions {
1755            uppercase: Some(false),
1756            ..FormatOptions::default()
1757        };
1758        let expected = indoc!(
1759            "
1760            select
1761              distinct *
1762            from
1763              foo
1764              left join bar
1765            where
1766              cola > 1
1767              and colb = 3"
1768        );
1769
1770        assert_eq!(format(input, &QueryParams::None, &options), expected);
1771    }
1772
1773    #[test]
1774    fn it_converts_keywords_nothing_when_no_option_passed_in() {
1775        let input = "select distinct * frOM foo left join bar WHERe cola > 1 and colb = 3";
1776        let options = FormatOptions {
1777            uppercase: None,
1778            ..FormatOptions::default()
1779        };
1780        let expected = indoc!(
1781            "
1782            select
1783              distinct *
1784            frOM
1785              foo
1786              left join bar
1787            WHERe
1788              cola > 1
1789              and colb = 3"
1790        );
1791
1792        assert_eq!(format(input, &QueryParams::None, &options), expected);
1793    }
1794    #[test]
1795    fn it_correctly_parses_all_operators() {
1796        let operators = [
1797            "!!", "!~~*", "!~~", "!~*", "!~", "##", "#>>", "#>", "#-", "&<|", "&<", "&>", "&&",
1798            "*<>", "*<=", "*>=", "*>", "*=", "*<", "<<|", "<<=", "<<", "<->", "<@", "<^", "<=",
1799            "<>", "<", ">=", ">>=", ">>", ">^", "->>", "->", "-|-", "-", "+", "/", "=", "%", "?||",
1800            "?|", "?-|", "?-", "?#", "?&", "?", "@@@", "@@", "@>", "@?", "@-@", "@", "^@", "^",
1801            "|&>", "|>>", "|/", "|", "||/", "||", "~>=~", "~>~", "~<=~", "~<~", "~=", "~*", "~~*",
1802            "~~", "~", "%", "<%", "%>", "<<%", "%>>", "<<->", "<->>", "<<<->", "<->>>",
1803        ];
1804
1805        // Test each operator individually
1806        for &operator in &operators {
1807            let input = format!("left {} right", operator);
1808            let expected = format!("left {} right", operator);
1809            let options = FormatOptions {
1810                uppercase: None,
1811                ..FormatOptions::default()
1812            };
1813
1814            assert_eq!(
1815                format(&input, &QueryParams::None, &options),
1816                expected,
1817                "Failed to parse operator: {}",
1818                operator
1819            );
1820        }
1821    }
1822    #[test]
1823    fn it_correctly_splits_operators() {
1824        let input = "
1825  SELECT
1826  left <@ right,
1827  left << right,
1828  left >> right,
1829  left &< right,
1830  left &> right,
1831  left -|- right,
1832  @@ left,
1833  @-@ left,
1834  left <-> right,
1835  left <<| right,
1836  left |>> right,
1837  left &<| right,
1838  left |>& right,
1839  left <^ right,
1840  left >^ right,
1841  left <% right,
1842  left %> right,
1843  ?- left,
1844  left ?-| right,
1845  left ?|| right,
1846  left ~= right";
1847        let options = FormatOptions {
1848            uppercase: None,
1849            ..FormatOptions::default()
1850        };
1851        let expected = indoc!(
1852            "
1853SELECT
1854  left <@ right,
1855  left << right,
1856  left >> right,
1857  left &< right,
1858  left &> right,
1859  left -|- right,
1860  @@ left,
1861  @-@ left,
1862  left <-> right,
1863  left <<| right,
1864  left |>> right,
1865  left &<| right,
1866  left |>& right,
1867  left <^ right,
1868  left >^ right,
1869  left <% right,
1870  left %> right,
1871  ?- left,
1872  left ?-| right,
1873  left ?|| right,
1874  left ~= right"
1875        );
1876
1877        assert_eq!(format(input, &QueryParams::None, &options), expected);
1878    }
1879    #[test]
1880    fn it_formats_double_colons() {
1881        let input = "select text  ::  text, num::integer, data::json, (x - y)::integer  frOM foo";
1882        let options = FormatOptions {
1883            uppercase: Some(false),
1884            ..FormatOptions::default()
1885        };
1886        let expected = indoc!(
1887            "
1888select
1889  text::text,
1890  num::integer,
1891  data::json,
1892  (x - y)::integer
1893from
1894  foo"
1895        );
1896
1897        assert_eq!(format(input, &QueryParams::None, &options), expected);
1898    }
1899}