├── .gitignore
├── LICENSE
├── README.md
├── cmd
└── sqlfmt
│ ├── main.go
│ └── sqlfmt_test.go
├── integration_test.go
├── kwlist.go
├── lex.go
├── parsed_types.go
├── renderer.go
├── renderer_test.go
├── sql.go
├── sql.y
├── testdata
├── arithmetic_expression.golden.sql
├── arithmetic_expression.input.sql
├── array_constructor.golden.sql
├── array_constructor.input.sql
├── array_index.golden.sql
├── array_index.input.sql
├── array_slice.golden.sql
├── array_slice.input.sql
├── array_subselect.golden.sql
├── array_subselect.input.sql
├── array_typecast.golden.sql
├── array_typecast.input.sql
├── at_time_zone.golden.sql
├── at_time_zone.input.sql
├── b_expr.golden.sql
├── b_expr.input.sql
├── between.golden.sql
├── between.input.sql
├── bitconst.golden.sql
├── bitconst.input.sql
├── boolean_binary_op.golden.sql
├── boolean_binary_op.input.sql
├── boolean_not.golden.sql
├── boolean_not.input.sql
├── case_full.golden.sql
├── case_full.input.sql
├── case_implicit.golden.sql
├── case_implicit.input.sql
├── cast_as.golden.sql
├── cast_as.input.sql
├── collate.golden.sql
├── collate.input.sql
├── collation_for.golden.sql
├── collation_for.input.sql
├── comment_beginning_single_line.golden.sql
├── comment_beginning_single_line.input.sql
├── comparison_expression.golden.sql
├── comparison_expression.input.sql
├── const_type_name.golden.sql
├── const_type_name.input.sql
├── custom_operators.golden.sql
├── custom_operators.input.sql
├── distinct.golden.sql
├── distinct.input.sql
├── distinct_on.golden.sql
├── distinct_on.input.sql
├── except.golden.sql
├── except.input.sql
├── exists.golden.sql
├── exists.input.sql
├── extract.golden.sql
├── extract.input.sql
├── float_constant.golden.sql
├── float_constant.input.sql
├── func_expr_expr_list.golden.sql
├── func_expr_expr_list.input.sql
├── func_expr_no_parens.golden.sql
├── func_expr_no_parens.input.sql
├── func_expr_one_arg.golden.sql
├── func_expr_one_arg.input.sql
├── function_call_qualified.golden.sql
├── function_call_qualified.input.sql
├── function_call_variadic.golden.sql
├── function_call_variadic.input.sql
├── function_call_with_all.golden.sql
├── function_call_with_all.input.sql
├── function_call_with_distinct.golden.sql
├── function_call_with_distinct.input.sql
├── function_call_with_filter.golden.sql
├── function_call_with_filter.input.sql
├── function_call_with_grouping_set.golden.sql
├── function_call_with_grouping_set.input.sql
├── function_call_with_order.golden.sql
├── function_call_with_order.input.sql
├── function_call_with_pg_named_args.golden.sql
├── function_call_with_pg_named_args.input.sql
├── function_call_with_positional_args.golden.sql
├── function_call_with_positional_args.input.sql
├── function_call_with_sql_named_args.golden.sql
├── function_call_with_sql_named_args.input.sql
├── function_call_with_star_arg.golden.sql
├── function_call_with_star_arg.input.sql
├── function_call_without_args.golden.sql
├── function_call_without_args.input.sql
├── group_by.golden.sql
├── group_by.input.sql
├── having.golden.sql
├── having.input.sql
├── in.golden.sql
├── in.input.sql
├── intersect.golden.sql
├── intersect.input.sql
├── interval.golden.sql
├── interval.input.sql
├── is_bool_op.golden.sql
├── is_bool_op.input.sql
├── is_distinct_from.golden.sql
├── is_distinct_from.input.sql
├── is_document.golden.sql
├── is_document.input.sql
├── is_null.golden.sql
├── is_null.input.sql
├── is_of_type_list.golden.sql
├── is_of_type_list.input.sql
├── like.golden.sql
├── like.input.sql
├── limit.golden.sql
├── limit.input.sql
├── limit_fetch.golden.sql
├── limit_fetch.input.sql
├── limit_offset.golden.sql
├── limit_offset.input.sql
├── null.golden.sql
├── null.input.sql
├── nullif.golden.sql
├── nullif.input.sql
├── offset.golden.sql
├── offset.input.sql
├── offset_fetch.golden.sql
├── offset_fetch.input.sql
├── offset_limit.golden.sql
├── offset_limit.input.sql
├── order.golden.sql
├── order.input.sql
├── order_column_num.golden.sql
├── order_column_num.input.sql
├── order_desc.golden.sql
├── order_desc.input.sql
├── order_multiple.golden.sql
├── order_multiple.input.sql
├── order_nulls.golden.sql
├── order_nulls.input.sql
├── order_using.golden.sql
├── order_using.input.sql
├── overlaps.golden.sql
├── overlaps.input.sql
├── overlay.golden.sql
├── overlay.input.sql
├── paren_expression.golden.sql
├── paren_expression.input.sql
├── position.golden.sql
├── position.input.sql
├── postfix_operator.golden.sql
├── postfix_operator.input.sql
├── quoted_identifier.golden.sql
├── quoted_identifier.input.sql
├── row.golden.sql
├── row.input.sql
├── select_for_key_share.golden.sql
├── select_for_key_share.input.sql
├── select_for_no_key_update.golden.sql
├── select_for_no_key_update.input.sql
├── select_for_share.golden.sql
├── select_for_share.input.sql
├── select_for_update.golden.sql
├── select_for_update.input.sql
├── select_for_update_nowait.golden.sql
├── select_for_update_nowait.input.sql
├── select_for_update_of.golden.sql
├── select_for_update_of.input.sql
├── select_from_aliased.golden.sql
├── select_from_aliased.input.sql
├── select_from_comma_join.golden.sql
├── select_from_comma_join.input.sql
├── select_from_cross_join.golden.sql
├── select_from_cross_join.input.sql
├── select_from_join_on.golden.sql
├── select_from_join_on.input.sql
├── select_from_join_using.golden.sql
├── select_from_join_using.input.sql
├── select_from_join_using_multiple.golden.sql
├── select_from_join_using_multiple.input.sql
├── select_from_left_join_on.golden.sql
├── select_from_left_join_on.input.sql
├── select_from_natural_join.golden.sql
├── select_from_natural_join.input.sql
├── select_into.golden.sql
├── select_into.input.sql
├── select_star.golden.sql
├── select_star.input.sql
├── select_table_dot_column.golden.sql
├── select_table_dot_column.input.sql
├── select_table_dot_star.golden.sql
├── select_table_dot_star.input.sql
├── select_where.golden.sql
├── select_where.input.sql
├── select_wrapped_by_parens.golden.sql
├── select_wrapped_by_parens.input.sql
├── semicolon.golden.sql
├── semicolon.input.sql
├── simple_select_literal_integer.golden.sql
├── simple_select_literal_integer.input.sql
├── simple_select_literal_text.golden.sql
├── simple_select_literal_text.input.sql
├── simple_select_with_from.golden.sql
├── simple_select_with_from.input.sql
├── simple_select_with_selection_alias.golden.sql
├── simple_select_with_selection_alias.input.sql
├── simple_select_with_selection_alias_no_as.golden.sql
├── simple_select_with_selection_alias_no_as.input.sql
├── simple_select_without_from.golden.sql
├── simple_select_without_from.input.sql
├── subquery_op.golden.sql
├── subquery_op.input.sql
├── subselect_expression.golden.sql
├── subselect_expression.input.sql
├── substring.golden.sql
├── substring.input.sql
├── table.golden.sql
├── table.input.sql
├── table_only.golden.sql
├── table_only.input.sql
├── table_only_paren.golden.sql
├── table_only_paren.input.sql
├── table_qualified.golden.sql
├── table_qualified.input.sql
├── table_star.golden.sql
├── table_star.input.sql
├── treat.golden.sql
├── treat.input.sql
├── trim.golden.sql
├── trim.input.sql
├── typecast.golden.sql
├── typecast.input.sql
├── unary.golden.sql
├── unary.input.sql
├── union.golden.sql
├── union.input.sql
├── values.golden.sql
├── values.input.sql
├── values_default.golden.sql
├── values_default.input.sql
├── values_order.golden.sql
├── values_order.input.sql
├── window_function.golden.sql
├── window_function.input.sql
├── window_function_frame.golden.sql
├── window_function_frame.input.sql
├── window_function_named.golden.sql
├── window_function_named.input.sql
├── window_function_named_multiple.golden.sql
├── window_function_named_multiple.input.sql
├── window_function_order_by.golden.sql
├── window_function_order_by.input.sql
├── window_function_partition_by.golden.sql
├── window_function_partition_by.input.sql
├── xmlelement.golden.sql
├── xmlelement.input.sql
├── xmlexists.golden.sql
├── xmlexists.input.sql
├── xmlforest.golden.sql
├── xmlforest.input.sql
├── xmlparse.golden.sql
├── xmlparse.input.sql
├── xmlpi.golden.sql
├── xmlpi.input.sql
├── xmlroot.golden.sql
├── xmlroot.input.sql
├── xmlserialize.golden.sql
└── xmlserialize.input.sql
├── token_renderer.go
└── token_renderer_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | /cmd/sqlfmt/sqlfmt
2 | /cmd/sqlfmt/tmp
3 | /y.output
4 | /tmp
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2015 Jack Christensen
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "{}"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright {yyyy} {name of copyright owner}
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
205 | -------------------------------------------------------------------------------
206 |
207 | Contains code derived from PostgreSQL.
208 |
209 | PostgreSQL Database Management System
210 | (formerly known as Postgres, then as Postgres95)
211 |
212 | Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
213 |
214 | Portions Copyright (c) 1994, The Regents of the University of California
215 |
216 | Permission to use, copy, modify, and distribute this software and its
217 | documentation for any purpose, without fee, and without a written agreement
218 | is hereby granted, provided that the above copyright notice and this
219 | paragraph and the following two paragraphs appear in all copies.
220 |
221 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
222 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
223 | LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
224 | DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
225 | POSSIBILITY OF SUCH DAMAGE.
226 |
227 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
228 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
229 | AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
230 | ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
231 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
232 |
233 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sqlfmt
2 |
3 | ## Installation
4 |
5 | ```sh
6 | $ go get github.com/jackc/sqlfmt/...
7 | $ which sqlfmt
8 | $GOPATH/bin/sqlfmt
9 | ```
10 |
11 | ## Usage
12 |
13 | - You can either:
14 |
15 | + Provide the path to one or more SQL files as command line arguments:
16 |
17 | ```sh
18 | $ sqlfmt testdata/select_where.input.sql
19 | select
20 | foo,
21 | bar
22 | from
23 | baz
24 | where
25 | foo > 5
26 | and bar < 2
27 | ```
28 |
29 | + Or, directly provide the SQL string via stdin:
30 |
31 | ```sh
32 | $ echo "select * from users" | sqlfmt
33 | select
34 | *
35 | from
36 | users
37 | ```
38 |
39 | ```sh
40 | $ sqlfmt < testdata/like.input.sql
41 | select
42 | foo,
43 | bar
44 | from
45 | baz
46 | where
47 | foo like 'abd%'
48 | or foo like 'ada%' escape '!'
49 | or foo not like 'abd%'
50 | or foo not like 'ada%' escape '!'
51 | or foo ilike 'efg%'
52 | or foo ilike 'ada%' escape '!'
53 | or foo not ilike 'efg%'
54 | or foo not ilike 'ada%' escape '!'
55 | ```
56 |
57 | - View [testdata](./testdata) for more examples.
58 |
--------------------------------------------------------------------------------
/cmd/sqlfmt/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "os"
9 | "path/filepath"
10 |
11 | "github.com/jackc/sqlfmt"
12 | )
13 |
14 | const Version = "0.1.0"
15 |
16 | var options struct {
17 | write bool
18 | upper bool
19 | version bool
20 | }
21 |
22 | type job struct {
23 | name string
24 | r io.ReadCloser
25 | w io.WriteCloser
26 | }
27 |
28 | func (j *job) run() error {
29 | if j.r == nil {
30 | var err error
31 | j.r, err = os.Open(j.name)
32 | if err != nil {
33 | return err
34 | }
35 | }
36 |
37 | input, err := ioutil.ReadAll(j.r)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | err = j.r.Close()
43 | if err != nil {
44 | return err
45 | }
46 |
47 | lexer := sqlfmt.NewSqlLexer(string(input))
48 | stmt, err := sqlfmt.Parse(lexer)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | var inPlace bool
54 | var tmpPath string
55 |
56 | if j.w == nil {
57 | dir := filepath.Dir(j.name)
58 | base := filepath.Base(j.name)
59 | tmpPath = filepath.Join(dir, "."+base+".sqlfmt")
60 | j.w, err = os.Create(tmpPath)
61 | if err != nil {
62 | return err
63 | }
64 | inPlace = true
65 | }
66 |
67 | r := sqlfmt.NewTextRenderer(j.w)
68 |
69 | r.UpperCase = options.upper
70 |
71 | stmt.RenderTo(r)
72 | if r.Error() != nil {
73 | return err
74 | }
75 |
76 | if inPlace {
77 | err = j.w.Close()
78 | if err != nil {
79 | return err
80 | }
81 | err = os.Rename(tmpPath, j.name)
82 | if err != nil {
83 | return err
84 | }
85 | }
86 |
87 | return nil
88 | }
89 |
90 | func main() {
91 | flag.Usage = func() {
92 | fmt.Fprintf(os.Stderr, "usage: %s [options] [path ...]\n", os.Args[0])
93 | flag.PrintDefaults()
94 | }
95 |
96 | flag.BoolVar(&options.write, "w", false, "write result to (source) file instead of stdout")
97 | flag.BoolVar(&options.upper, "u", false, "format with upper-case")
98 | flag.BoolVar(&options.version, "version", false, "print version and exit")
99 | flag.Parse()
100 |
101 | if options.version {
102 | fmt.Printf("sqlfmt v%v\n", Version)
103 | os.Exit(0)
104 | }
105 |
106 | var jobs []job
107 |
108 | if len(flag.Args()) > 0 {
109 | for _, fp := range flag.Args() {
110 | j := job{name: fp}
111 | if !options.write {
112 | j.w = os.Stdout
113 | }
114 | jobs = append(jobs, j)
115 | }
116 | } else {
117 | jobs = append(jobs, job{r: os.Stdin, w: os.Stdout})
118 | }
119 |
120 | var errors []error
121 |
122 | for _, j := range jobs {
123 | if err := j.run(); err != nil {
124 | errors = append(errors, err)
125 | }
126 | }
127 |
128 | if len(errors) > 0 {
129 | for _, e := range errors {
130 | fmt.Fprintln(os.Stderr, e)
131 | }
132 | os.Exit(1)
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/cmd/sqlfmt/sqlfmt_test.go:
--------------------------------------------------------------------------------
1 | package main_test
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "os/exec"
9 | "path/filepath"
10 | "runtime"
11 | "testing"
12 | )
13 |
14 | func TestMain(m *testing.M) {
15 | os.MkdirAll("tmp", os.ModeDir|os.ModePerm)
16 | exe := "tmp/sqlfmt"
17 | if runtime.GOOS == "windows" {
18 | exe += ".exe"
19 | }
20 | err := exec.Command("go", "build", "-o", exe).Run()
21 | if err != nil {
22 | fmt.Println("Failed to build sqlfmt binary:", err)
23 | os.Exit(1)
24 | }
25 |
26 | os.Exit(m.Run())
27 | }
28 |
29 | func sqlfmt(sql []byte, args ...string) ([]byte, error) {
30 | cmd := exec.Command("tmp/sqlfmt", args...)
31 | stdin, err := cmd.StdinPipe()
32 | if err != nil {
33 | return nil, fmt.Errorf("cmd.StdinPipe failed: %v", err)
34 | }
35 | stdout, err := cmd.StdoutPipe()
36 | if err != nil {
37 | return nil, fmt.Errorf("cmd.StdoutPipe failed: %v", err)
38 | }
39 | stderr, err := cmd.StderrPipe()
40 | if err != nil {
41 | return nil, fmt.Errorf("cmd.StderrPipe failed: %v", err)
42 | }
43 |
44 | err = cmd.Start()
45 | if err != nil {
46 | return nil, fmt.Errorf("cmd.Start failed: %v", err)
47 | }
48 |
49 | _, err = stdin.Write(sql)
50 | if err != nil {
51 | return nil, fmt.Errorf("stdin.Write failed: %v", err)
52 | }
53 |
54 | err = stdin.Close()
55 | if err != nil {
56 | return nil, fmt.Errorf("stdin.Close failed: %v", err)
57 | }
58 |
59 | output, err := ioutil.ReadAll(stdout)
60 | if err != nil {
61 | return nil, fmt.Errorf("ioutil.ReadAll(stdout) failed: %v", err)
62 | }
63 |
64 | errout, err := ioutil.ReadAll(stderr)
65 | if err != nil {
66 | return nil, fmt.Errorf("ioutil.ReadAll(stderr) failed: %v", err)
67 | }
68 |
69 | err = cmd.Wait()
70 | if err != nil {
71 | return nil, fmt.Errorf("cmd.Wait failed: %v\n%s", err, string(errout))
72 | }
73 |
74 | return output, nil
75 | }
76 |
77 | func TestFileInput(t *testing.T) {
78 | inputFile := "simple_select_without_from.input.sql"
79 | expectedOutputFile := "simple_select_without_from.golden.sql"
80 |
81 | expected, err := ioutil.ReadFile(filepath.Join("../../testdata", expectedOutputFile))
82 | if err != nil {
83 | t.Fatal(err)
84 | }
85 |
86 | filePath := filepath.Join("../../testdata", inputFile)
87 | output, err := sqlfmt(nil, filePath)
88 | if err != nil {
89 | t.Fatalf("sqlfmt failed with %s: %v", filePath, err)
90 | }
91 |
92 | if bytes.Compare(output, expected) != 0 {
93 | actualFileName := filepath.Join("tmp", "TestFileInput.sql")
94 | err = ioutil.WriteFile(actualFileName, output, os.ModePerm)
95 | if err != nil {
96 | t.Fatal(err)
97 | }
98 |
99 | t.Errorf("Given %s, did not receive %s. Unexpected output written to %s", filePath, expectedOutputFile, actualFileName)
100 | }
101 | }
102 |
103 | func TestFileFormatInPlace(t *testing.T) {
104 | inputFile := "simple_select_without_from.input.sql"
105 | expectedOutputFile := "simple_select_without_from.golden.sql"
106 | expectedOutputPath := filepath.Join("../../testdata", expectedOutputFile)
107 |
108 | expected, err := ioutil.ReadFile(expectedOutputPath)
109 | if err != nil {
110 | t.Fatal(err)
111 | }
112 |
113 | sourcePath := filepath.Join("../../testdata", inputFile)
114 | source, err := ioutil.ReadFile(sourcePath)
115 | if err != nil {
116 | t.Fatal(err)
117 | }
118 |
119 | tmpFilePath := filepath.Join("tmp", inputFile)
120 | err = ioutil.WriteFile(tmpFilePath, source, os.ModePerm)
121 | if err != nil {
122 | t.Fatal(err)
123 | }
124 |
125 | output, err := sqlfmt(nil, "-w", tmpFilePath)
126 | if err != nil {
127 | t.Fatalf("sqlfmt failed with %s: %v", tmpFilePath, err)
128 | }
129 |
130 | if len(output) != 0 {
131 | t.Fatal("Expected output to be empty, but it wasn't")
132 | }
133 |
134 | output, err = ioutil.ReadFile(tmpFilePath)
135 | if err != nil {
136 | t.Fatal(err)
137 | }
138 |
139 | if bytes.Compare(output, expected) != 0 {
140 | t.Errorf("Given %s, did not receive %s. Unexpected output written to %s", sourcePath, expectedOutputPath, tmpFilePath)
141 | }
142 | }
143 |
144 | func TestMultipleFileFormatInPlace(t *testing.T) {
145 | tests := []struct {
146 | Name string
147 | Source []byte
148 | Expected []byte
149 | TmpFilePath string
150 | }{
151 | {Name: "between"},
152 | {Name: "bitconst"},
153 | }
154 |
155 | var err error
156 | args := []string{"-w"}
157 | for i, _ := range tests {
158 | sourcePath := filepath.Join("../../testdata", tests[i].Name+".input.sql")
159 | tests[i].Source, err = ioutil.ReadFile(sourcePath)
160 | if err != nil {
161 | t.Fatal(err)
162 | }
163 |
164 | expectedOutputPath := filepath.Join("../../testdata", tests[i].Name+".golden.sql")
165 | tests[i].Expected, err = ioutil.ReadFile(expectedOutputPath)
166 | if err != nil {
167 | t.Fatal(err)
168 | }
169 |
170 | tests[i].TmpFilePath = filepath.Join("tmp", tests[i].Name+".sql")
171 | err = ioutil.WriteFile(tests[i].TmpFilePath, tests[i].Source, os.ModePerm)
172 | if err != nil {
173 | t.Fatal(err)
174 | }
175 |
176 | args = append(args, tests[i].TmpFilePath)
177 | }
178 |
179 | output, err := sqlfmt(nil, args...)
180 | if err != nil {
181 | t.Fatalf("sqlfmt failed with %s: %v", args, err)
182 | }
183 |
184 | if len(output) != 0 {
185 | t.Fatal("Expected output to be empty, but it wasn't")
186 | }
187 |
188 | for _, tt := range tests {
189 | output, err = ioutil.ReadFile(tt.TmpFilePath)
190 | if err != nil {
191 | t.Fatal(err)
192 | }
193 |
194 | if bytes.Compare(output, tt.Expected) != 0 {
195 | t.Errorf("%s: unexpected output written to %s", tt.Name, tt.TmpFilePath)
196 | }
197 | }
198 | }
199 |
200 | func TestSqlFmtAll(t *testing.T) {
201 | fileInfos, err := ioutil.ReadDir("../../testdata")
202 | if err != nil {
203 | t.Fatal(err)
204 | }
205 |
206 | for _, fi := range fileInfos {
207 | if fi.Name()[len(fi.Name())-10:] != ".input.sql" {
208 | continue
209 | }
210 |
211 | testName := fi.Name()[:len(fi.Name())-10]
212 | inputPath := filepath.Join("../../testdata", fi.Name())
213 | goldenPath := filepath.Join("../../testdata", testName+".golden.sql")
214 |
215 | input, err := ioutil.ReadFile(inputPath)
216 | if err != nil {
217 | t.Errorf("%s: %v", testName, err)
218 | continue
219 | }
220 |
221 | expected, err := ioutil.ReadFile(goldenPath)
222 | if err != nil {
223 | t.Errorf("%s: %v", testName, err)
224 | continue
225 | }
226 |
227 | output, err := sqlfmt(input)
228 | if err != nil {
229 | t.Errorf("%s: sqlfmt failed with: %v", testName, err)
230 | continue
231 | }
232 |
233 | if bytes.Compare(output, expected) != 0 {
234 | actualFileName := filepath.Join("tmp", fmt.Sprintf("%s.sql", testName))
235 | err = ioutil.WriteFile(actualFileName, output, os.ModePerm)
236 | if err != nil {
237 | t.Fatal(err)
238 | }
239 |
240 | t.Errorf("%s: Unexpected output written to %s", testName, actualFileName)
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/integration_test.go:
--------------------------------------------------------------------------------
1 | package sqlfmt_test
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 | "testing"
10 |
11 | "github.com/jackc/sqlfmt"
12 | )
13 |
14 | func TestIntegration(t *testing.T) {
15 | // TODO - put these notes in testdata input files when sqlfmt handles comments
16 | // {inputFile: "b_expr.sql"}, // b_expr is duplicated subset of a_expr -- test its clauses
17 | // {inputFile: "in.sql"}, // TODO - fix formatting when spacing / new line is improved
18 |
19 | fileInfos, err := ioutil.ReadDir("testdata")
20 | if err != nil {
21 | t.Fatal(err)
22 | }
23 |
24 | for _, fi := range fileInfos {
25 | if fi.Name()[len(fi.Name())-10:] != ".input.sql" {
26 | continue
27 | }
28 |
29 | testName := fi.Name()[:len(fi.Name())-10]
30 | inputPath := filepath.Join("testdata", fi.Name())
31 | goldenPath := filepath.Join("testdata", testName+".golden.sql")
32 |
33 | input, err := ioutil.ReadFile(inputPath)
34 | if err != nil {
35 | t.Errorf("%s: %v", testName, err)
36 | continue
37 | }
38 |
39 | expected, err := ioutil.ReadFile(goldenPath)
40 | if err != nil {
41 | t.Errorf("%s: %v", testName, err)
42 | continue
43 | }
44 |
45 | lexer := sqlfmt.NewSqlLexer(string(input))
46 | stmt, err := sqlfmt.Parse(lexer)
47 | if err != nil {
48 | t.Errorf("%s: Given %s, %v", testName, inputPath, err)
49 | continue
50 | }
51 |
52 | var outBuf bytes.Buffer
53 | r := sqlfmt.NewTextRenderer(&outBuf)
54 | stmt.RenderTo(r)
55 |
56 | if outBuf.String() != string(expected) {
57 | actualFileName := filepath.Join("tmp", fmt.Sprintf("%s.sql", testName))
58 | err = ioutil.WriteFile(actualFileName, outBuf.Bytes(), os.ModePerm)
59 | if err != nil {
60 | t.Fatal(err)
61 | }
62 |
63 | t.Errorf("%s: Unexpected output written to %s", testName, actualFileName)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/kwlist.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | // Derived from PostgreSQL -- ./src/include/parser/kwlist.h
4 |
5 | var keywords map[string]int
6 |
7 | func init() {
8 | keywords = make(map[string]int)
9 | /* name, value, category */
10 | keywords["abort"] = ABORT_P
11 | keywords["absolute"] = ABSOLUTE_P
12 | keywords["access"] = ACCESS
13 | keywords["action"] = ACTION
14 | keywords["add"] = ADD_P
15 | keywords["admin"] = ADMIN
16 | keywords["after"] = AFTER
17 | keywords["aggregate"] = AGGREGATE
18 | keywords["all"] = ALL
19 | keywords["also"] = ALSO
20 | keywords["alter"] = ALTER
21 | keywords["always"] = ALWAYS
22 | keywords["analyse"] = ANALYSE /* British spelling */
23 | keywords["analyze"] = ANALYZE
24 | keywords["and"] = AND
25 | keywords["any"] = ANY
26 | keywords["array"] = ARRAY
27 | keywords["as"] = AS
28 | keywords["asc"] = ASC
29 | keywords["assertion"] = ASSERTION
30 | keywords["assignment"] = ASSIGNMENT
31 | keywords["asymmetric"] = ASYMMETRIC
32 | keywords["at"] = AT
33 | keywords["attribute"] = ATTRIBUTE
34 | keywords["authorization"] = AUTHORIZATION
35 | keywords["backward"] = BACKWARD
36 | keywords["before"] = BEFORE
37 | keywords["begin"] = BEGIN_P
38 | keywords["between"] = BETWEEN
39 | keywords["bigint"] = BIGINT
40 | keywords["binary"] = BINARY
41 | keywords["bit"] = BIT
42 | keywords["boolean"] = BOOLEAN_P
43 | keywords["both"] = BOTH
44 | keywords["by"] = BY
45 | keywords["cache"] = CACHE
46 | keywords["called"] = CALLED
47 | keywords["cascade"] = CASCADE
48 | keywords["cascaded"] = CASCADED
49 | keywords["case"] = CASE
50 | keywords["cast"] = CAST
51 | keywords["catalog"] = CATALOG_P
52 | keywords["chain"] = CHAIN
53 | keywords["char"] = CHAR_P
54 | keywords["character"] = CHARACTER
55 | keywords["characteristics"] = CHARACTERISTICS
56 | keywords["check"] = CHECK
57 | keywords["checkpoint"] = CHECKPOINT
58 | keywords["class"] = CLASS
59 | keywords["close"] = CLOSE
60 | keywords["cluster"] = CLUSTER
61 | keywords["coalesce"] = COALESCE
62 | keywords["collate"] = COLLATE
63 | keywords["collation"] = COLLATION
64 | keywords["column"] = COLUMN
65 | keywords["comment"] = COMMENT
66 | keywords["comments"] = COMMENTS
67 | keywords["commit"] = COMMIT
68 | keywords["committed"] = COMMITTED
69 | keywords["concurrently"] = CONCURRENTLY
70 | keywords["configuration"] = CONFIGURATION
71 | keywords["conflict"] = CONFLICT
72 | keywords["connection"] = CONNECTION
73 | keywords["constraint"] = CONSTRAINT
74 | keywords["constraints"] = CONSTRAINTS
75 | keywords["content"] = CONTENT_P
76 | keywords["continue"] = CONTINUE_P
77 | keywords["conversion"] = CONVERSION_P
78 | keywords["copy"] = COPY
79 | keywords["cost"] = COST
80 | keywords["create"] = CREATE
81 | keywords["cross"] = CROSS
82 | keywords["csv"] = CSV
83 | keywords["cube"] = CUBE
84 | keywords["current"] = CURRENT_P
85 | keywords["current_catalog"] = CURRENT_CATALOG
86 | keywords["current_date"] = CURRENT_DATE
87 | keywords["current_role"] = CURRENT_ROLE
88 | keywords["current_schema"] = CURRENT_SCHEMA
89 | keywords["current_time"] = CURRENT_TIME
90 | keywords["current_timestamp"] = CURRENT_TIMESTAMP
91 | keywords["current_user"] = CURRENT_USER
92 | keywords["cursor"] = CURSOR
93 | keywords["cycle"] = CYCLE
94 | keywords["data"] = DATA_P
95 | keywords["database"] = DATABASE
96 | keywords["day"] = DAY_P
97 | keywords["deallocate"] = DEALLOCATE
98 | keywords["dec"] = DEC
99 | keywords["decimal"] = DECIMAL_P
100 | keywords["declare"] = DECLARE
101 | keywords["default"] = DEFAULT
102 | keywords["defaults"] = DEFAULTS
103 | keywords["deferrable"] = DEFERRABLE
104 | keywords["deferred"] = DEFERRED
105 | keywords["definer"] = DEFINER
106 | keywords["delete"] = DELETE_P
107 | keywords["delimiter"] = DELIMITER
108 | keywords["delimiters"] = DELIMITERS
109 | keywords["desc"] = DESC
110 | keywords["dictionary"] = DICTIONARY
111 | keywords["disable"] = DISABLE_P
112 | keywords["discard"] = DISCARD
113 | keywords["distinct"] = DISTINCT
114 | keywords["do"] = DO
115 | keywords["document"] = DOCUMENT_P
116 | keywords["domain"] = DOMAIN_P
117 | keywords["double"] = DOUBLE_P
118 | keywords["drop"] = DROP
119 | keywords["each"] = EACH
120 | keywords["else"] = ELSE
121 | keywords["enable"] = ENABLE_P
122 | keywords["encoding"] = ENCODING
123 | keywords["encrypted"] = ENCRYPTED
124 | keywords["end"] = END_P
125 | keywords["enum"] = ENUM_P
126 | keywords["escape"] = ESCAPE
127 | keywords["event"] = EVENT
128 | keywords["except"] = EXCEPT
129 | keywords["exclude"] = EXCLUDE
130 | keywords["excluding"] = EXCLUDING
131 | keywords["exclusive"] = EXCLUSIVE
132 | keywords["execute"] = EXECUTE
133 | keywords["exists"] = EXISTS
134 | keywords["explain"] = EXPLAIN
135 | keywords["extension"] = EXTENSION
136 | keywords["external"] = EXTERNAL
137 | keywords["extract"] = EXTRACT
138 | keywords["false"] = FALSE_P
139 | keywords["family"] = FAMILY
140 | keywords["fetch"] = FETCH
141 | keywords["filter"] = FILTER
142 | keywords["first"] = FIRST_P
143 | keywords["float"] = FLOAT_P
144 | keywords["following"] = FOLLOWING
145 | keywords["for"] = FOR
146 | keywords["force"] = FORCE
147 | keywords["foreign"] = FOREIGN
148 | keywords["forward"] = FORWARD
149 | keywords["freeze"] = FREEZE
150 | keywords["from"] = FROM
151 | keywords["full"] = FULL
152 | keywords["function"] = FUNCTION
153 | keywords["functions"] = FUNCTIONS
154 | keywords["global"] = GLOBAL
155 | keywords["grant"] = GRANT
156 | keywords["granted"] = GRANTED
157 | keywords["greatest"] = GREATEST
158 | keywords["group"] = GROUP_P
159 | keywords["grouping"] = GROUPING
160 | keywords["handler"] = HANDLER
161 | keywords["having"] = HAVING
162 | keywords["header"] = HEADER_P
163 | keywords["hold"] = HOLD
164 | keywords["hour"] = HOUR_P
165 | keywords["identity"] = IDENTITY_P
166 | keywords["if"] = IF_P
167 | keywords["ilike"] = ILIKE
168 | keywords["immediate"] = IMMEDIATE
169 | keywords["immutable"] = IMMUTABLE
170 | keywords["implicit"] = IMPLICIT_P
171 | keywords["import"] = IMPORT_P
172 | keywords["in"] = IN_P
173 | keywords["including"] = INCLUDING
174 | keywords["increment"] = INCREMENT
175 | keywords["index"] = INDEX
176 | keywords["indexes"] = INDEXES
177 | keywords["inherit"] = INHERIT
178 | keywords["inherits"] = INHERITS
179 | keywords["initially"] = INITIALLY
180 | keywords["inline"] = INLINE_P
181 | keywords["inner"] = INNER_P
182 | keywords["inout"] = INOUT
183 | keywords["input"] = INPUT_P
184 | keywords["insensitive"] = INSENSITIVE
185 | keywords["insert"] = INSERT
186 | keywords["instead"] = INSTEAD
187 | keywords["int"] = INT_P
188 | keywords["integer"] = INTEGER
189 | keywords["intersect"] = INTERSECT
190 | keywords["interval"] = INTERVAL
191 | keywords["into"] = INTO
192 | keywords["invoker"] = INVOKER
193 | keywords["is"] = IS
194 | keywords["isnull"] = ISNULL
195 | keywords["isolation"] = ISOLATION
196 | keywords["join"] = JOIN
197 | keywords["key"] = KEY
198 | keywords["label"] = LABEL
199 | keywords["language"] = LANGUAGE
200 | keywords["large"] = LARGE_P
201 | keywords["last"] = LAST_P
202 | keywords["lateral"] = LATERAL_P
203 | keywords["leading"] = LEADING
204 | keywords["leakproof"] = LEAKPROOF
205 | keywords["least"] = LEAST
206 | keywords["left"] = LEFT
207 | keywords["level"] = LEVEL
208 | keywords["like"] = LIKE
209 | keywords["limit"] = LIMIT
210 | keywords["listen"] = LISTEN
211 | keywords["load"] = LOAD
212 | keywords["local"] = LOCAL
213 | keywords["localtime"] = LOCALTIME
214 | keywords["localtimestamp"] = LOCALTIMESTAMP
215 | keywords["location"] = LOCATION
216 | keywords["lock"] = LOCK_P
217 | keywords["locked"] = LOCKED
218 | keywords["logged"] = LOGGED
219 | keywords["mapping"] = MAPPING
220 | keywords["match"] = MATCH
221 | keywords["materialized"] = MATERIALIZED
222 | keywords["maxvalue"] = MAXVALUE
223 | keywords["minute"] = MINUTE_P
224 | keywords["minvalue"] = MINVALUE
225 | keywords["mode"] = MODE
226 | keywords["month"] = MONTH_P
227 | keywords["move"] = MOVE
228 | keywords["name"] = NAME_P
229 | keywords["names"] = NAMES
230 | keywords["national"] = NATIONAL
231 | keywords["natural"] = NATURAL
232 | keywords["nchar"] = NCHAR
233 | keywords["next"] = NEXT
234 | keywords["no"] = NO
235 | keywords["none"] = NONE
236 | keywords["not"] = NOT
237 | keywords["nothing"] = NOTHING
238 | keywords["notify"] = NOTIFY
239 | keywords["notnull"] = NOTNULL
240 | keywords["nowait"] = NOWAIT
241 | keywords["null"] = NULL_P
242 | keywords["nullif"] = NULLIF
243 | keywords["nulls"] = NULLS_P
244 | keywords["numeric"] = NUMERIC
245 | keywords["object"] = OBJECT_P
246 | keywords["of"] = OF
247 | keywords["off"] = OFF
248 | keywords["offset"] = OFFSET
249 | keywords["oids"] = OIDS
250 | keywords["on"] = ON
251 | keywords["only"] = ONLY
252 | keywords["operator"] = OPERATOR
253 | keywords["option"] = OPTION
254 | keywords["options"] = OPTIONS
255 | keywords["or"] = OR
256 | keywords["order"] = ORDER
257 | keywords["ordinality"] = ORDINALITY
258 | keywords["out"] = OUT_P
259 | keywords["outer"] = OUTER_P
260 | keywords["over"] = OVER
261 | keywords["overlaps"] = OVERLAPS
262 | keywords["overlay"] = OVERLAY
263 | keywords["owned"] = OWNED
264 | keywords["owner"] = OWNER
265 | keywords["parser"] = PARSER
266 | keywords["partial"] = PARTIAL
267 | keywords["partition"] = PARTITION
268 | keywords["passing"] = PASSING
269 | keywords["password"] = PASSWORD
270 | keywords["placing"] = PLACING
271 | keywords["plans"] = PLANS
272 | keywords["policy"] = POLICY
273 | keywords["position"] = POSITION
274 | keywords["preceding"] = PRECEDING
275 | keywords["precision"] = PRECISION
276 | keywords["prepare"] = PREPARE
277 | keywords["prepared"] = PREPARED
278 | keywords["preserve"] = PRESERVE
279 | keywords["primary"] = PRIMARY
280 | keywords["prior"] = PRIOR
281 | keywords["privileges"] = PRIVILEGES
282 | keywords["procedural"] = PROCEDURAL
283 | keywords["procedure"] = PROCEDURE
284 | keywords["program"] = PROGRAM
285 | keywords["quote"] = QUOTE
286 | keywords["range"] = RANGE
287 | keywords["read"] = READ
288 | keywords["real"] = REAL
289 | keywords["reassign"] = REASSIGN
290 | keywords["recheck"] = RECHECK
291 | keywords["recursive"] = RECURSIVE
292 | keywords["ref"] = REF
293 | keywords["references"] = REFERENCES
294 | keywords["refresh"] = REFRESH
295 | keywords["reindex"] = REINDEX
296 | keywords["relative"] = RELATIVE_P
297 | keywords["release"] = RELEASE
298 | keywords["rename"] = RENAME
299 | keywords["repeatable"] = REPEATABLE
300 | keywords["replace"] = REPLACE
301 | keywords["replica"] = REPLICA
302 | keywords["reset"] = RESET
303 | keywords["restart"] = RESTART
304 | keywords["restrict"] = RESTRICT
305 | keywords["returning"] = RETURNING
306 | keywords["returns"] = RETURNS
307 | keywords["revoke"] = REVOKE
308 | keywords["right"] = RIGHT
309 | keywords["role"] = ROLE
310 | keywords["rollback"] = ROLLBACK
311 | keywords["rollup"] = ROLLUP
312 | keywords["row"] = ROW
313 | keywords["rows"] = ROWS
314 | keywords["rule"] = RULE
315 | keywords["savepoint"] = SAVEPOINT
316 | keywords["schema"] = SCHEMA
317 | keywords["scroll"] = SCROLL
318 | keywords["search"] = SEARCH
319 | keywords["second"] = SECOND_P
320 | keywords["security"] = SECURITY
321 | keywords["select"] = SELECT
322 | keywords["sequence"] = SEQUENCE
323 | keywords["sequences"] = SEQUENCES
324 | keywords["serializable"] = SERIALIZABLE
325 | keywords["server"] = SERVER
326 | keywords["session"] = SESSION
327 | keywords["session_user"] = SESSION_USER
328 | keywords["set"] = SET
329 | keywords["setof"] = SETOF
330 | keywords["sets"] = SETS
331 | keywords["share"] = SHARE
332 | keywords["show"] = SHOW
333 | keywords["similar"] = SIMILAR
334 | keywords["simple"] = SIMPLE
335 | keywords["skip"] = SKIP
336 | keywords["smallint"] = SMALLINT
337 | keywords["snapshot"] = SNAPSHOT
338 | keywords["some"] = SOME
339 | keywords["sql"] = SQL_P
340 | keywords["stable"] = STABLE
341 | keywords["standalone"] = STANDALONE_P
342 | keywords["start"] = START
343 | keywords["statement"] = STATEMENT
344 | keywords["statistics"] = STATISTICS
345 | keywords["stdin"] = STDIN
346 | keywords["stdout"] = STDOUT
347 | keywords["storage"] = STORAGE
348 | keywords["strict"] = STRICT_P
349 | keywords["strip"] = STRIP_P
350 | keywords["substring"] = SUBSTRING
351 | keywords["symmetric"] = SYMMETRIC
352 | keywords["sysid"] = SYSID
353 | keywords["system"] = SYSTEM_P
354 | keywords["table"] = TABLE
355 | keywords["tables"] = TABLES
356 | keywords["tablesample"] = TABLESAMPLE
357 | keywords["tablespace"] = TABLESPACE
358 | keywords["temp"] = TEMP
359 | keywords["template"] = TEMPLATE
360 | keywords["temporary"] = TEMPORARY
361 | keywords["text"] = TEXT_P
362 | keywords["then"] = THEN
363 | keywords["time"] = TIME
364 | keywords["timestamp"] = TIMESTAMP
365 | keywords["to"] = TO
366 | keywords["trailing"] = TRAILING
367 | keywords["transaction"] = TRANSACTION
368 | keywords["transform"] = TRANSFORM
369 | keywords["treat"] = TREAT
370 | keywords["trigger"] = TRIGGER
371 | keywords["trim"] = TRIM
372 | keywords["true"] = TRUE_P
373 | keywords["truncate"] = TRUNCATE
374 | keywords["trusted"] = TRUSTED
375 | keywords["type"] = TYPE_P
376 | keywords["types"] = TYPES_P
377 | keywords["unbounded"] = UNBOUNDED
378 | keywords["uncommitted"] = UNCOMMITTED
379 | keywords["unencrypted"] = UNENCRYPTED
380 | keywords["union"] = UNION
381 | keywords["unique"] = UNIQUE
382 | keywords["unknown"] = UNKNOWN
383 | keywords["unlisten"] = UNLISTEN
384 | keywords["unlogged"] = UNLOGGED
385 | keywords["until"] = UNTIL
386 | keywords["update"] = UPDATE
387 | keywords["user"] = USER
388 | keywords["using"] = USING
389 | keywords["vacuum"] = VACUUM
390 | keywords["valid"] = VALID
391 | keywords["validate"] = VALIDATE
392 | keywords["validator"] = VALIDATOR
393 | keywords["value"] = VALUE_P
394 | keywords["values"] = VALUES
395 | keywords["varchar"] = VARCHAR
396 | keywords["variadic"] = VARIADIC
397 | keywords["varying"] = VARYING
398 | keywords["verbose"] = VERBOSE
399 | keywords["version"] = VERSION_P
400 | keywords["view"] = VIEW
401 | keywords["views"] = VIEWS
402 | keywords["volatile"] = VOLATILE
403 | keywords["when"] = WHEN
404 | keywords["where"] = WHERE
405 | keywords["whitespace"] = WHITESPACE_P
406 | keywords["window"] = WINDOW
407 | keywords["with"] = WITH
408 | keywords["within"] = WITHIN
409 | keywords["without"] = WITHOUT
410 | keywords["work"] = WORK
411 | keywords["wrapper"] = WRAPPER
412 | keywords["write"] = WRITE
413 | keywords["xml"] = XML_P
414 | keywords["xmlattributes"] = XMLATTRIBUTES
415 | keywords["xmlconcat"] = XMLCONCAT
416 | keywords["xmlelement"] = XMLELEMENT
417 | keywords["xmlexists"] = XMLEXISTS
418 | keywords["xmlforest"] = XMLFOREST
419 | keywords["xmlparse"] = XMLPARSE
420 | keywords["xmlpi"] = XMLPI
421 | keywords["xmlroot"] = XMLROOT
422 | keywords["xmlserialize"] = XMLSERIALIZE
423 | keywords["year"] = YEAR_P
424 | keywords["yes"] = YES_P
425 | keywords["zone"] = ZONE
426 | }
427 |
--------------------------------------------------------------------------------
/lex.go:
--------------------------------------------------------------------------------
1 | //go:generate -command yacc go tool yacc
2 | //go:generate yacc -o sql.go sql.y
3 | package sqlfmt
4 |
5 | import (
6 | "log"
7 | "strings"
8 | "unicode"
9 | "unicode/utf8"
10 | )
11 |
12 | type stateFn func(*sqlLex) stateFn
13 |
14 | type token struct {
15 | typ int
16 | src string
17 | }
18 |
19 | type sqlLex struct {
20 | src string
21 | start int
22 | pos int
23 | width int
24 | state stateFn
25 | tokens []token
26 | stmt *SelectStmt
27 | }
28 |
29 | func (x *sqlLex) Lex(yylval *yySymType) int {
30 | token := x.tokens[0]
31 | x.tokens = x.tokens[1:]
32 |
33 | yylval.str = token.src
34 | return token.typ
35 | }
36 |
37 | // The parser calls this method on a parse error.
38 | func (x *sqlLex) Error(s string) {
39 | log.Printf("parse error: %s at character %d", s, x.start)
40 | }
41 |
42 | func NewSqlLexer(src string) *sqlLex {
43 | x := &sqlLex{src: src,
44 | tokens: make([]token, 0),
45 | state: blankState,
46 | }
47 |
48 | for x.state != nil {
49 | x.state = x.state(x)
50 | }
51 |
52 | x.append(token{typ: eof})
53 |
54 | return x
55 | }
56 |
57 | func (l *sqlLex) append(t token) {
58 | l.tokens = append(l.tokens, t)
59 |
60 | if len(l.tokens) == 1 {
61 | return
62 | }
63 |
64 | prevToken := &l.tokens[len(l.tokens)-2]
65 |
66 | switch t.typ {
67 | case BETWEEN, IN_P, LIKE, ILIKE, SIMILAR:
68 | if prevToken.typ == NOT {
69 | prevToken.typ = NOT_LA
70 | }
71 | case FIRST_P, LAST_P:
72 | if prevToken.typ == NULLS_P {
73 | prevToken.typ = NULLS_LA
74 | }
75 | case TIME, ORDINALITY:
76 | if prevToken.typ == WITH {
77 | prevToken.typ = WITH_LA
78 | }
79 | }
80 | }
81 |
82 | func (l *sqlLex) next() (r rune) {
83 | if l.pos >= len(l.src) {
84 | l.width = 0 // because backing up from having read eof should read eof again
85 | return 0
86 | }
87 |
88 | r, l.width = utf8.DecodeRuneInString(l.src[l.pos:])
89 | l.pos += l.width
90 |
91 | return r
92 | }
93 |
94 | func (l *sqlLex) unnext() {
95 | l.pos -= l.width
96 | }
97 |
98 | func (l *sqlLex) ignore() {
99 | l.start = l.pos
100 | }
101 |
102 | func (l *sqlLex) acceptRunFunc(f func(rune) bool) {
103 | for f(l.next()) {
104 | }
105 | l.unnext()
106 | }
107 |
108 | func blankState(l *sqlLex) stateFn {
109 | switch r := l.next(); {
110 | case r == 0:
111 | return nil
112 | case r == ',' || r == '(' || r == ')' || r == '[' || r == ']' || r == ';':
113 | return lexSimple
114 | case r == '\'':
115 | return lexStringConst
116 | case r == '"':
117 | return lexQuotedIdentifier
118 | case r == ':' || r == '.':
119 | return lexAlmostOperator
120 | case isOperator(r):
121 | return lexOperator
122 | case r == 'b' || r == 'B' || r == 'x' || r == 'X':
123 | return lexPossibleBitString
124 | case unicode.IsDigit(r):
125 | return lexNumber
126 | case isWhitespace(r):
127 | l.skipWhitespace()
128 | return blankState
129 | case isAlphanumeric(r):
130 | return lexAlphanumeric
131 | }
132 | return nil
133 | }
134 |
135 | func lexNumber(l *sqlLex) stateFn {
136 | typ := ICONST
137 | l.acceptRunFunc(unicode.IsDigit)
138 | r := l.next()
139 | if r == '.' {
140 | l.acceptRunFunc(unicode.IsDigit)
141 | typ = FCONST
142 | } else {
143 | l.unnext()
144 | }
145 | t := token{src: l.src[l.start:l.pos], typ: typ}
146 | l.append(t)
147 | l.start = l.pos
148 | return blankState
149 | }
150 |
151 | func lexAlphanumeric(l *sqlLex) stateFn {
152 | l.acceptRunFunc(isAlphanumeric)
153 |
154 | t := token{src: l.src[l.start:l.pos]}
155 |
156 | if typ, ok := keywords[strings.ToLower(t.src)]; ok {
157 | t.typ = typ
158 | } else {
159 | t.typ = IDENT
160 | }
161 |
162 | l.append(t)
163 | l.start = l.pos
164 | return blankState
165 | }
166 |
167 | func lexStringConst(l *sqlLex) stateFn {
168 | for {
169 | var r rune
170 | r = l.next()
171 | if r == 0 {
172 | return nil // error for EOF inside of string literal
173 | }
174 |
175 | if r == '\'' {
176 | r = l.next()
177 | if r != '\'' {
178 | l.unnext()
179 | t := token{src: l.src[l.start:l.pos]}
180 | t.typ = SCONST
181 | l.append(t)
182 | l.start = l.pos
183 | return blankState
184 | }
185 | }
186 | }
187 | }
188 |
189 | func lexQuotedIdentifier(l *sqlLex) stateFn {
190 | for {
191 | var r rune
192 | r = l.next()
193 | if r == 0 {
194 | return nil // error for EOF inside of string literal
195 | }
196 |
197 | if r == '"' {
198 | r = l.next()
199 | if r != '"' {
200 | l.unnext()
201 | t := token{src: l.src[l.start:l.pos]}
202 | t.typ = IDENT
203 | l.append(t)
204 | l.start = l.pos
205 | return blankState
206 | }
207 | }
208 | }
209 | }
210 |
211 | // lexAlmostOperator is for operator-like ':' and '.' which aren't operators
212 | func lexAlmostOperator(l *sqlLex) stateFn {
213 | l.next()
214 |
215 | t := token{src: l.src[l.start:l.pos]}
216 | switch {
217 | case t.src == "::":
218 | t.typ = TYPECAST
219 | case t.src == "..":
220 | t.typ = DOT_DOT
221 | case t.src == ":=":
222 | t.typ = COLON_EQUALS
223 | default:
224 | l.unnext()
225 | t.typ = int(t.src[0])
226 | }
227 | t.src = l.src[l.start:l.pos]
228 |
229 | l.append(t)
230 | l.start = l.pos
231 | return blankState
232 | }
233 |
234 | func lexOperator(l *sqlLex) stateFn {
235 | l.acceptRunFunc(isOperator)
236 |
237 | if (l.pos-l.start) >= 2 && l.src[l.start:l.start+2] == "--" {
238 | return lexDashDashComment
239 | }
240 |
241 | t := token{src: l.src[l.start:l.pos]}
242 | switch {
243 | case t.src == "+" || t.src == "-" || t.src == "*" || t.src == "/" || t.src == "%" || t.src == "^" || t.src == "<" || t.src == ">" || t.src == "=" || t.src == "[" || t.src == "]" || t.src == ":":
244 | t.typ = int(t.src[0])
245 | case t.src == "=>":
246 | t.typ = EQUALS_GREATER
247 | case t.src == "<=":
248 | t.typ = LESS_EQUALS
249 | case t.src == ">=":
250 | t.typ = GREATER_EQUALS
251 | case t.src == "<>", t.src == "!=":
252 | t.typ = NOT_EQUALS
253 | default:
254 | t.typ = Op
255 | }
256 |
257 | l.append(t)
258 | l.start = l.pos
259 | return blankState
260 | }
261 |
262 | func lexSimple(l *sqlLex) stateFn {
263 | l.append(token{int(l.src[l.start]), l.src[l.start:l.pos]})
264 | l.start = l.pos
265 | return blankState
266 | }
267 |
268 | func lexPossibleBitString(l *sqlLex) stateFn {
269 | var rangeTable unicode.RangeTable
270 |
271 | if l.src[l.start] == 'b' || l.src[l.start] == 'B' {
272 | rangeTable = unicode.RangeTable{
273 | R16: []unicode.Range16{
274 | unicode.Range16{Lo: '0', Hi: '1', Stride: 1},
275 | },
276 | LatinOffset: 1,
277 | }
278 | } else {
279 | rangeTable = unicode.RangeTable{
280 | R16: []unicode.Range16{
281 | unicode.Range16{Lo: '0', Hi: '9', Stride: 1},
282 | unicode.Range16{Lo: 'A', Hi: 'F', Stride: 1},
283 | unicode.Range16{Lo: 'a', Hi: 'f', Stride: 1},
284 | },
285 | LatinOffset: 3,
286 | }
287 | }
288 |
289 | r := l.next()
290 | if r == '\'' {
291 | l.acceptRunFunc(func(r rune) bool {
292 | return unicode.In(r, &rangeTable)
293 | })
294 | r = l.next()
295 | if r == '\'' {
296 | t := token{src: l.src[l.start:l.pos], typ: BCONST}
297 | l.append(t)
298 | l.start = l.pos
299 | return blankState
300 | }
301 | return nil // lex error
302 | } else {
303 | l.unnext()
304 | }
305 |
306 | return lexAlphanumeric
307 | }
308 |
309 | func lexDashDashComment(l *sqlLex) stateFn {
310 | var r rune
311 | for r = l.next(); r != '\n'; r = l.next() {
312 | }
313 |
314 | // TODO - don't ignore comments
315 | l.ignore()
316 |
317 | return blankState
318 | }
319 |
320 | func (l *sqlLex) skipWhitespace() {
321 | var r rune
322 | for r = l.next(); isWhitespace(r); r = l.next() {
323 | }
324 |
325 | if r != 0 {
326 | l.unnext()
327 | }
328 |
329 | l.ignore()
330 | }
331 |
332 | func isWhitespace(r rune) bool {
333 | return unicode.IsSpace(r)
334 | }
335 |
336 | func isAlphanumeric(r rune) bool {
337 | return r == '_' || unicode.In(r, unicode.Letter, unicode.Digit)
338 | }
339 |
340 | func isOperator(r rune) bool {
341 | // list of operator characters from
342 | // http://www.postgresql.org/docs/9.4/static/sql-createoperator.html
343 | return strings.IndexRune("+-*/<>=~!@#%^&|`?", r) != -1
344 | }
345 |
--------------------------------------------------------------------------------
/parsed_types.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | func Parse(lexer *sqlLex) (stmt *SelectStmt, err error) {
8 | if rc := yyParse(lexer); rc != 0 {
9 | return nil, errors.New("Parse failed")
10 | }
11 |
12 | return lexer.stmt, nil
13 | }
14 |
15 | type Expr interface {
16 | RenderTo(Renderer)
17 | }
18 |
19 | type PgType struct {
20 | Name AnyName
21 | OptInterval *OptInterval
22 | Setof bool
23 | ArrayWord bool
24 | ArrayBounds []IntegerConst
25 | TypeMods []Expr
26 | CharSet string
27 | WithTimeZone bool
28 | }
29 |
30 | func (t PgType) RenderTo(r Renderer) {
31 | if t.Setof {
32 | r.Text("setof", KeywordToken)
33 | }
34 |
35 | t.Name.RenderTo(r)
36 |
37 | if t.OptInterval != nil {
38 | t.OptInterval.RenderTo(r)
39 | }
40 |
41 | if t.ArrayWord {
42 | r.Text("array", KeywordToken)
43 | }
44 |
45 | for _, ab := range t.ArrayBounds {
46 | r.Text("[", SymbolToken)
47 | r.Text(string(ab), ConstantToken)
48 | r.Text("]", SymbolToken)
49 | }
50 |
51 | if len(t.TypeMods) > 0 {
52 | r.Text("(", SymbolToken)
53 | for i, e := range t.TypeMods {
54 | e.RenderTo(r)
55 | if i < len(t.TypeMods)-1 {
56 | r.Text(",", SymbolToken)
57 | }
58 | }
59 | r.Text(")", SymbolToken)
60 | }
61 |
62 | if t.WithTimeZone {
63 | r.Text("with time zone", KeywordToken)
64 | }
65 |
66 | if t.CharSet != "" {
67 | r.Text("character set", KeywordToken)
68 | r.Text(t.CharSet, IdentifierToken)
69 | }
70 | }
71 |
72 | type AnyName []string
73 |
74 | func (an AnyName) RenderTo(r Renderer) {
75 | for i, n := range an {
76 | r.Text(n, IdentifierToken)
77 | if i < len(an)-1 {
78 | r.Text(".", SymbolToken)
79 | }
80 | }
81 | }
82 |
83 | type ColumnRef struct {
84 | Name string
85 | Indirection Indirection
86 | }
87 |
88 | func (cr ColumnRef) RenderTo(r Renderer) {
89 | r.Text(cr.Name, IdentifierToken)
90 | if cr.Indirection != nil {
91 | cr.Indirection.RenderTo(r)
92 | }
93 | }
94 |
95 | type Indirection []IndirectionEl
96 |
97 | func (i Indirection) RenderTo(r Renderer) {
98 | for _, e := range i {
99 | e.RenderTo(r)
100 | }
101 | }
102 |
103 | type IndirectionEl struct {
104 | Name string
105 | LowerSubscript Expr
106 | UpperSubscript Expr
107 | }
108 |
109 | func (ie IndirectionEl) RenderTo(r Renderer) {
110 | if ie.LowerSubscript != nil {
111 | r.Text("[", SymbolToken)
112 | ie.LowerSubscript.RenderTo(r)
113 | if ie.UpperSubscript != nil {
114 | r.Text(":", SymbolToken)
115 | ie.UpperSubscript.RenderTo(r)
116 | }
117 | r.Text("]", SymbolToken)
118 | } else {
119 | r.Text(".", SymbolToken)
120 | r.Text(ie.Name, IdentifierToken)
121 | }
122 | }
123 |
124 | type StringConst string
125 |
126 | func (s StringConst) RenderTo(r Renderer) {
127 | r.Text(string(s), ConstantToken)
128 | }
129 |
130 | type IntegerConst string
131 |
132 | func (s IntegerConst) RenderTo(r Renderer) {
133 | r.Text(string(s), ConstantToken)
134 | }
135 |
136 | type FloatConst string
137 |
138 | func (s FloatConst) RenderTo(r Renderer) {
139 | r.Text(string(s), ConstantToken)
140 | }
141 |
142 | type BoolConst bool
143 |
144 | func (b BoolConst) RenderTo(r Renderer) {
145 | if b {
146 | r.Text("true", KeywordToken)
147 | } else {
148 | r.Text("false", KeywordToken)
149 | }
150 | }
151 |
152 | type NullConst struct{}
153 |
154 | func (n NullConst) RenderTo(r Renderer) {
155 | r.Text("null", KeywordToken)
156 | }
157 |
158 | type BitConst string
159 |
160 | func (b BitConst) RenderTo(r Renderer) {
161 | r.Text(string(b), ConstantToken)
162 | }
163 |
164 | type BooleanExpr struct {
165 | Left Expr
166 | Operator string
167 | Right Expr
168 | }
169 |
170 | func (e BooleanExpr) RenderTo(r Renderer) {
171 | e.Left.RenderTo(r)
172 | r.Control(NewLineToken)
173 | r.Text(e.Operator, SymbolToken)
174 | e.Right.RenderTo(r)
175 | }
176 |
177 | type BinaryExpr struct {
178 | Left Expr
179 | Operator AnyName
180 | Right Expr
181 | }
182 |
183 | func (e BinaryExpr) RenderTo(r Renderer) {
184 | e.Left.RenderTo(r)
185 | r.Control(SpaceToken)
186 | e.Operator.RenderTo(r)
187 | r.Control(SpaceToken)
188 | e.Right.RenderTo(r)
189 | }
190 |
191 | type ArrayConstructorExpr ArrayExpr
192 |
193 | func (ace ArrayConstructorExpr) RenderTo(r Renderer) {
194 | r.Text("array", KeywordToken)
195 | ArrayExpr(ace).RenderTo(r)
196 | }
197 |
198 | type ArrayExpr []Expr
199 |
200 | func (a ArrayExpr) RenderTo(r Renderer) {
201 | r.Text("[", SymbolToken)
202 | for i, e := range a {
203 | e.RenderTo(r)
204 | if i < len(a)-1 {
205 | r.Text(",", SymbolToken)
206 | }
207 | }
208 | r.Text("]", SymbolToken)
209 | }
210 |
211 | type TextOpWithEscapeExpr struct {
212 | Left Expr
213 | Operator string
214 | Right Expr
215 | Escape Expr
216 | }
217 |
218 | func (e TextOpWithEscapeExpr) RenderTo(r Renderer) {
219 | e.Left.RenderTo(r)
220 | r.Text(e.Operator, SymbolToken)
221 | e.Right.RenderTo(r)
222 |
223 | if e.Escape != nil {
224 | r.Text("escape", KeywordToken)
225 | e.Escape.RenderTo(r)
226 | }
227 | }
228 |
229 | type UnaryExpr struct {
230 | Operator AnyName
231 | Expr Expr
232 | }
233 |
234 | func (e UnaryExpr) RenderTo(r Renderer) {
235 | e.Operator.RenderTo(r)
236 | r.Control(RefuseSpaceToken)
237 | e.Expr.RenderTo(r)
238 | }
239 |
240 | type PostfixExpr struct {
241 | Expr Expr
242 | Operator AnyName
243 | }
244 |
245 | func (e PostfixExpr) RenderTo(r Renderer) {
246 | e.Expr.RenderTo(r)
247 | e.Operator.RenderTo(r)
248 | }
249 |
250 | type SubqueryOpExpr struct {
251 | Value Expr
252 | Op SubqueryOp
253 | Type string
254 | Query Expr
255 | }
256 |
257 | func (s SubqueryOpExpr) RenderTo(r Renderer) {
258 | s.Value.RenderTo(r)
259 | s.Op.RenderTo(r)
260 | r.Text(s.Type, KeywordToken)
261 | r.Control(SpaceToken)
262 | s.Query.RenderTo(r)
263 | }
264 |
265 | type SubqueryOp struct {
266 | Operator bool
267 | Name AnyName
268 | }
269 |
270 | func (s SubqueryOp) RenderTo(r Renderer) {
271 | if s.Operator {
272 | r.Text("operator", KeywordToken)
273 | r.Text("(", SymbolToken)
274 | }
275 | s.Name.RenderTo(r)
276 | if s.Operator {
277 | r.Text(")", SymbolToken)
278 | }
279 | }
280 |
281 | type WhenClause struct {
282 | When Expr
283 | Then Expr
284 | }
285 |
286 | func (w WhenClause) RenderTo(r Renderer) {
287 | r.Text("when", KeywordToken)
288 | w.When.RenderTo(r)
289 | r.Text("then", KeywordToken)
290 | r.Control(NewLineToken)
291 | r.Control(IndentToken)
292 | w.Then.RenderTo(r)
293 | r.Control(NewLineToken)
294 | r.Control(UnindentToken)
295 | }
296 |
297 | type InExpr struct {
298 | Value Expr
299 | Not bool
300 | In Expr
301 | }
302 |
303 | func (i InExpr) RenderTo(r Renderer) {
304 | i.Value.RenderTo(r)
305 |
306 | if i.Not {
307 | r.Text("not", KeywordToken)
308 | }
309 |
310 | r.Text("in", KeywordToken)
311 | r.Control(SpaceToken)
312 |
313 | i.In.RenderTo(r)
314 | }
315 |
316 | type BetweenExpr struct {
317 | Expr Expr
318 | Not bool
319 | Symmetric bool
320 | Left Expr
321 | Right Expr
322 | }
323 |
324 | func (b BetweenExpr) RenderTo(r Renderer) {
325 | b.Expr.RenderTo(r)
326 |
327 | if b.Not {
328 | r.Text("not", KeywordToken)
329 | }
330 |
331 | r.Text("between", KeywordToken)
332 |
333 | if b.Symmetric {
334 | r.Text("symmetric", KeywordToken)
335 | }
336 |
337 | b.Left.RenderTo(r)
338 | r.Text("and", KeywordToken)
339 | b.Right.RenderTo(r)
340 | }
341 |
342 | type CaseExpr struct {
343 | CaseArg Expr
344 | WhenClauses []WhenClause
345 | Default Expr
346 | }
347 |
348 | func (c CaseExpr) RenderTo(r Renderer) {
349 | r.Text("case", KeywordToken)
350 |
351 | if c.CaseArg != nil {
352 | c.CaseArg.RenderTo(r)
353 | }
354 |
355 | r.Control(NewLineToken)
356 |
357 | for _, w := range c.WhenClauses {
358 | w.RenderTo(r)
359 | }
360 |
361 | if c.Default != nil {
362 | r.Text("else", KeywordToken)
363 | r.Control(NewLineToken)
364 | r.Control(IndentToken)
365 | c.Default.RenderTo(r)
366 | r.Control(NewLineToken)
367 | r.Control(UnindentToken)
368 | }
369 |
370 | r.Text("end", KeywordToken)
371 | r.Control(NewLineToken)
372 | }
373 |
374 | type ParenExpr struct {
375 | Expr Expr
376 | Indirection Indirection
377 | }
378 |
379 | func (e ParenExpr) RenderTo(r Renderer) {
380 | r.Text("(", SymbolToken)
381 | e.Expr.RenderTo(r)
382 | r.Text(")", SymbolToken)
383 | if e.Indirection != nil {
384 | e.Indirection.RenderTo(r)
385 | }
386 | }
387 |
388 | type TypecastExpr struct {
389 | Expr Expr
390 | Typename PgType
391 | }
392 |
393 | func (t TypecastExpr) RenderTo(r Renderer) {
394 | t.Expr.RenderTo(r)
395 | r.Text("::", SymbolToken)
396 | t.Typename.RenderTo(r)
397 | }
398 |
399 | type ConstTypeExpr struct {
400 | Typename PgType
401 | Expr Expr
402 | }
403 |
404 | func (t ConstTypeExpr) RenderTo(r Renderer) {
405 | t.Typename.RenderTo(r)
406 | t.Expr.RenderTo(r)
407 | }
408 |
409 | type ConstIntervalExpr struct {
410 | Precision IntegerConst
411 | Value Expr
412 | OptInterval *OptInterval
413 | }
414 |
415 | func (i ConstIntervalExpr) RenderTo(r Renderer) {
416 | r.Text("interval", KeywordToken)
417 | if i.Precision != "" {
418 | r.Text("(", SymbolToken)
419 | i.Precision.RenderTo(r)
420 | r.Text(")", SymbolToken)
421 | }
422 |
423 | i.Value.RenderTo(r)
424 |
425 | if i.OptInterval != nil {
426 | i.OptInterval.RenderTo(r)
427 | }
428 | }
429 |
430 | type OptInterval struct {
431 | Left string
432 | Right string
433 | Second *IntervalSecond
434 | }
435 |
436 | func (oi OptInterval) RenderTo(r Renderer) {
437 | if oi.Left != "" {
438 | r.Text(oi.Left, KeywordToken)
439 | }
440 |
441 | if oi.Right != "" {
442 | r.Text("to", KeywordToken)
443 | r.Text(oi.Right, KeywordToken)
444 | }
445 |
446 | if oi.Second != nil {
447 | oi.Second.RenderTo(r)
448 | }
449 | }
450 |
451 | type IntervalSecond struct {
452 | Precision IntegerConst
453 | }
454 |
455 | func (is IntervalSecond) RenderTo(r Renderer) {
456 | r.Text("second", KeywordToken)
457 | if is.Precision != "" {
458 | r.Text("(", SymbolToken)
459 | is.Precision.RenderTo(r)
460 | r.Text(")", SymbolToken)
461 | }
462 | }
463 |
464 | type ExtractExpr ExtractList
465 |
466 | func (ee ExtractExpr) RenderTo(r Renderer) {
467 | r.Text("extract", KeywordToken)
468 | r.Text("(", SymbolToken)
469 | ExtractList(ee).RenderTo(r)
470 | r.Text(")", SymbolToken)
471 | }
472 |
473 | type ExtractList struct {
474 | Extract Expr
475 | Time Expr
476 | }
477 |
478 | func (el ExtractList) RenderTo(r Renderer) {
479 | el.Extract.RenderTo(r)
480 | r.Text("from", KeywordToken)
481 | el.Time.RenderTo(r)
482 | }
483 |
484 | type OverlayExpr OverlayList
485 |
486 | func (oe OverlayExpr) RenderTo(r Renderer) {
487 | r.Text("overlay", KeywordToken)
488 | r.Text("(", SymbolToken)
489 | OverlayList(oe).RenderTo(r)
490 | r.Text(")", SymbolToken)
491 | }
492 |
493 | type OverlayList struct {
494 | Dest Expr
495 | Placing Expr
496 | From Expr
497 | For Expr
498 | }
499 |
500 | func (ol OverlayList) RenderTo(r Renderer) {
501 | ol.Dest.RenderTo(r)
502 | r.Text("placing", KeywordToken)
503 | ol.Placing.RenderTo(r)
504 | r.Text("from", KeywordToken)
505 | ol.From.RenderTo(r)
506 |
507 | if ol.For != nil {
508 | r.Text("for", KeywordToken)
509 | ol.For.RenderTo(r)
510 | }
511 | }
512 |
513 | type PositionExpr PositionList
514 |
515 | func (pe PositionExpr) RenderTo(r Renderer) {
516 | r.Text("position", KeywordToken)
517 | r.Text("(", SymbolToken)
518 | PositionList(pe).RenderTo(r)
519 | r.Text(")", SymbolToken)
520 | }
521 |
522 | type PositionList struct {
523 | Substring Expr
524 | String Expr
525 | }
526 |
527 | func (pl PositionList) RenderTo(r Renderer) {
528 | pl.Substring.RenderTo(r)
529 | r.Text("in", KeywordToken)
530 | pl.String.RenderTo(r)
531 | }
532 |
533 | type SubstrExpr SubstrList
534 |
535 | func (se SubstrExpr) RenderTo(r Renderer) {
536 | r.Text("substring", KeywordToken)
537 | r.Text("(", SymbolToken)
538 | SubstrList(se).RenderTo(r)
539 | r.Text(")", SymbolToken)
540 | }
541 |
542 | type SubstrList struct {
543 | Source Expr
544 | From Expr
545 | For Expr
546 | }
547 |
548 | func (sl SubstrList) RenderTo(r Renderer) {
549 | sl.Source.RenderTo(r)
550 | r.Text("from", KeywordToken)
551 | sl.From.RenderTo(r)
552 |
553 | if sl.For != nil {
554 | r.Text("for", KeywordToken)
555 | sl.For.RenderTo(r)
556 | }
557 | }
558 |
559 | type TrimExpr struct {
560 | Direction string
561 | TrimList
562 | }
563 |
564 | func (te TrimExpr) RenderTo(r Renderer) {
565 | r.Text("trim", KeywordToken)
566 | r.Text("(", SymbolToken)
567 | if te.Direction != "" {
568 | r.Text(te.Direction, KeywordToken)
569 | }
570 | te.TrimList.RenderTo(r)
571 | r.Text(")", SymbolToken)
572 | }
573 |
574 | type TrimList struct {
575 | Left Expr
576 | From bool
577 | Right []Expr
578 | }
579 |
580 | func (tl TrimList) RenderTo(r Renderer) {
581 | if tl.Left != nil {
582 | tl.Left.RenderTo(r)
583 | }
584 |
585 | if tl.From {
586 | r.Text("from", KeywordToken)
587 | }
588 |
589 | for i, e := range tl.Right {
590 | e.RenderTo(r)
591 | if i+1 < len(tl.Right) {
592 | r.Text(",", SymbolToken)
593 | }
594 | }
595 | }
596 |
597 | type XmlElement struct {
598 | Name string
599 | Attributes XmlAttributes
600 | Body []Expr
601 | }
602 |
603 | func (el XmlElement) RenderTo(r Renderer) {
604 | r.Text("xmlelement", KeywordToken)
605 | r.Text("(", SymbolToken)
606 | r.Text("name", KeywordToken)
607 | r.Text(el.Name, IdentifierToken)
608 |
609 | if el.Attributes != nil {
610 | r.Text(",", SymbolToken)
611 | el.Attributes.RenderTo(r)
612 | }
613 |
614 | if el.Body != nil {
615 | for _, e := range el.Body {
616 | r.Text(",", SymbolToken)
617 | e.RenderTo(r)
618 | }
619 | }
620 |
621 | r.Text(")", SymbolToken)
622 | }
623 |
624 | type XmlAttributes []XmlAttributeEl
625 |
626 | func (attrs XmlAttributes) RenderTo(r Renderer) {
627 | r.Text("xmlattributes", KeywordToken)
628 | r.Text("(", SymbolToken)
629 | xmlAttributes(attrs).RenderTo(r)
630 | r.Text(")", SymbolToken)
631 | }
632 |
633 | type XmlAttributeEl struct {
634 | Value Expr
635 | Name string
636 | }
637 |
638 | func (el XmlAttributeEl) RenderTo(r Renderer) {
639 | el.Value.RenderTo(r)
640 | if el.Name != "" {
641 | r.Text("as", KeywordToken)
642 | r.Text(el.Name, IdentifierToken)
643 | }
644 | }
645 |
646 | type XmlExists struct {
647 | Path Expr
648 | Body XmlExistsArgument
649 | }
650 |
651 | func (e XmlExists) RenderTo(r Renderer) {
652 | r.Text("xmlexists", KeywordToken)
653 | r.Text("(", SymbolToken)
654 | e.Path.RenderTo(r)
655 | e.Body.RenderTo(r)
656 | r.Text(")", SymbolToken)
657 | }
658 |
659 | type XmlExistsArgument struct {
660 | LeftByRef bool
661 | Arg Expr
662 | RightByRef bool
663 | }
664 |
665 | func (a XmlExistsArgument) RenderTo(r Renderer) {
666 | r.Text("passing", KeywordToken)
667 |
668 | if a.LeftByRef {
669 | r.Text("by ref", KeywordToken)
670 | }
671 |
672 | a.Arg.RenderTo(r)
673 |
674 | if a.RightByRef {
675 | r.Text("by ref", KeywordToken)
676 | }
677 | }
678 |
679 | type XmlForest []XmlAttributeEl
680 |
681 | func (f XmlForest) RenderTo(r Renderer) {
682 | r.Text("xmlforest", KeywordToken)
683 | r.Text("(", SymbolToken)
684 | xmlAttributes(f).RenderTo(r)
685 | r.Text(")", SymbolToken)
686 | }
687 |
688 | type xmlAttributes []XmlAttributeEl
689 |
690 | func (attrs xmlAttributes) RenderTo(r Renderer) {
691 | for i, a := range attrs {
692 | a.RenderTo(r)
693 | if i+1 < len(attrs) {
694 | r.Text(",", SymbolToken)
695 | }
696 | }
697 | }
698 |
699 | type XmlParse struct {
700 | Type string
701 | Content Expr
702 | WhitespaceOption string
703 | }
704 |
705 | func (p XmlParse) RenderTo(r Renderer) {
706 | r.Text("xmlparse", KeywordToken)
707 | r.Text("(", SymbolToken)
708 | r.Text(p.Type, KeywordToken)
709 | p.Content.RenderTo(r)
710 | if p.WhitespaceOption != "" {
711 | r.Text(p.WhitespaceOption, KeywordToken)
712 | }
713 |
714 | r.Text(")", SymbolToken)
715 | }
716 |
717 | type XmlPi struct {
718 | Name string
719 | Content Expr
720 | }
721 |
722 | func (p XmlPi) RenderTo(r Renderer) {
723 | r.Text("xmlpi", KeywordToken)
724 | r.Text("(", SymbolToken)
725 | r.Text("name", KeywordToken)
726 | r.Text(p.Name, IdentifierToken)
727 |
728 | if p.Content != nil {
729 | r.Text(",", SymbolToken)
730 | p.Content.RenderTo(r)
731 | }
732 | r.Text(")", SymbolToken)
733 | }
734 |
735 | type XmlRoot struct {
736 | Xml Expr
737 | Version XmlRootVersion
738 | Standalone string
739 | }
740 |
741 | func (x XmlRoot) RenderTo(r Renderer) {
742 | r.Text("xmlroot", KeywordToken)
743 | r.Text("(", SymbolToken)
744 | x.Xml.RenderTo(r)
745 | r.Text(",", SymbolToken)
746 | x.Version.RenderTo(r)
747 | if x.Standalone != "" {
748 | r.Text(",", SymbolToken)
749 | r.Text("standalone", KeywordToken)
750 | r.Text(x.Standalone, KeywordToken)
751 | }
752 | r.Text(")", SymbolToken)
753 | }
754 |
755 | type XmlRootVersion struct {
756 | Expr Expr
757 | }
758 |
759 | func (rv XmlRootVersion) RenderTo(r Renderer) {
760 | r.Text("version", KeywordToken)
761 | if rv.Expr != nil {
762 | rv.Expr.RenderTo(r)
763 | } else {
764 | r.Text("no value", KeywordToken)
765 | }
766 | }
767 |
768 | type XmlSerialize struct {
769 | XmlType string
770 | Content Expr
771 | Type PgType
772 | }
773 |
774 | func (s XmlSerialize) RenderTo(r Renderer) {
775 | r.Text("xmlserialize", KeywordToken)
776 | r.Text("(", SymbolToken)
777 | r.Text(s.XmlType, KeywordToken)
778 | s.Content.RenderTo(r)
779 | r.Text("as", KeywordToken)
780 | s.Type.RenderTo(r)
781 | r.Text(")", SymbolToken)
782 | }
783 |
784 | type CollateExpr struct {
785 | Expr Expr
786 | Collation AnyName
787 | }
788 |
789 | func (c CollateExpr) RenderTo(r Renderer) {
790 | c.Expr.RenderTo(r)
791 | r.Text("collate", KeywordToken)
792 | c.Collation.RenderTo(r)
793 | }
794 |
795 | type NotExpr struct {
796 | Expr Expr
797 | }
798 |
799 | func (e NotExpr) RenderTo(r Renderer) {
800 | r.Text("not", KeywordToken)
801 | e.Expr.RenderTo(r)
802 | }
803 |
804 | type IsExpr struct {
805 | Expr Expr
806 | Not bool
807 | Op string // null, document, true, false, etc.
808 | }
809 |
810 | func (e IsExpr) RenderTo(r Renderer) {
811 | e.Expr.RenderTo(r)
812 | r.Text("is", KeywordToken)
813 | if e.Not {
814 | r.Text("not", KeywordToken)
815 | }
816 | r.Text(e.Op, KeywordToken)
817 | }
818 |
819 | type AliasedExpr struct {
820 | Expr Expr
821 | Alias string
822 | }
823 |
824 | func (e AliasedExpr) RenderTo(r Renderer) {
825 | e.Expr.RenderTo(r)
826 | r.Text("as", KeywordToken)
827 | r.Text(e.Alias, IdentifierToken)
828 | }
829 |
830 | type IntoClause struct {
831 | Options string
832 | OptTable bool
833 | Target AnyName
834 | }
835 |
836 | func (i IntoClause) RenderTo(r Renderer) {
837 | r.Text("into", KeywordToken)
838 |
839 | if i.Options != "" {
840 | r.Text(i.Options, KeywordToken)
841 | }
842 |
843 | if i.OptTable {
844 | r.Text("table", KeywordToken)
845 | }
846 |
847 | i.Target.RenderTo(r)
848 | r.Control(NewLineToken)
849 | }
850 |
851 | type FromClause struct {
852 | Expr Expr
853 | }
854 |
855 | func (e FromClause) RenderTo(r Renderer) {
856 | r.Text("from", KeywordToken)
857 | r.Control(NewLineToken)
858 | r.Control(IndentToken)
859 | e.Expr.RenderTo(r)
860 | r.Control(NewLineToken)
861 | r.Control(UnindentToken)
862 | }
863 |
864 | type JoinExpr struct {
865 | Left Expr
866 | Join string
867 | Right Expr
868 | Using []string
869 | On Expr
870 | }
871 |
872 | func (s JoinExpr) RenderTo(r Renderer) {
873 | s.Left.RenderTo(r)
874 |
875 | if s.Join == "," {
876 | r.Text(",", SymbolToken)
877 | r.Control(NewLineToken)
878 | } else {
879 | r.Control(NewLineToken)
880 | r.Text(s.Join, KeywordToken)
881 | }
882 |
883 | s.Right.RenderTo(r)
884 |
885 | if len(s.Using) > 0 {
886 | r.Text("using", KeywordToken)
887 | r.Text("(", SymbolToken)
888 |
889 | for i, u := range s.Using {
890 | r.Text(u, IdentifierToken)
891 | if i+1 < len(s.Using) {
892 | r.Text(",", SymbolToken)
893 | }
894 | }
895 |
896 | r.Text(")", SymbolToken)
897 | }
898 |
899 | if s.On != nil {
900 | r.Text("on", KeywordToken)
901 | s.On.RenderTo(r)
902 | }
903 | }
904 |
905 | type WhereClause struct {
906 | Expr Expr
907 | }
908 |
909 | func (e WhereClause) RenderTo(r Renderer) {
910 | r.Text("where", KeywordToken)
911 | r.Control(NewLineToken)
912 | r.Control(IndentToken)
913 | e.Expr.RenderTo(r)
914 | r.Control(NewLineToken)
915 | r.Control(UnindentToken)
916 | }
917 |
918 | type OrderExpr struct {
919 | Expr Expr
920 | Order string
921 | Using AnyName
922 | Nulls string
923 | }
924 |
925 | func (e OrderExpr) RenderTo(r Renderer) {
926 | e.Expr.RenderTo(r)
927 | if e.Order != "" {
928 | r.Text(e.Order, KeywordToken)
929 | }
930 | if len(e.Using) > 0 {
931 | r.Text("using", KeywordToken)
932 | e.Using.RenderTo(r)
933 | }
934 | if e.Nulls != "" {
935 | r.Text("nulls", KeywordToken)
936 | r.Text(e.Nulls, KeywordToken)
937 | }
938 | }
939 |
940 | type OrderClause struct {
941 | Exprs []OrderExpr
942 | }
943 |
944 | func (e OrderClause) RenderTo(r Renderer) {
945 | r.Text("order by", KeywordToken)
946 | r.Control(NewLineToken)
947 | r.Control(IndentToken)
948 |
949 | for i, f := range e.Exprs {
950 | f.RenderTo(r)
951 | if i < len(e.Exprs)-1 {
952 | r.Text(",", SymbolToken)
953 | }
954 | r.Control(NewLineToken)
955 | }
956 | r.Control(UnindentToken)
957 | }
958 |
959 | type GroupByClause struct {
960 | Exprs []Expr
961 | }
962 |
963 | func (e GroupByClause) RenderTo(r Renderer) {
964 | r.Text("group by", KeywordToken)
965 | r.Control(NewLineToken)
966 | r.Control(IndentToken)
967 |
968 | for i, f := range e.Exprs {
969 | f.RenderTo(r)
970 | if i < len(e.Exprs)-1 {
971 | r.Text(",", SymbolToken)
972 | }
973 | r.Control(NewLineToken)
974 | }
975 | r.Control(UnindentToken)
976 | }
977 |
978 | type LimitClause struct {
979 | Limit Expr
980 | Offset Expr
981 | }
982 |
983 | func (e LimitClause) RenderTo(r Renderer) {
984 | if e.Limit != nil {
985 | r.Text("limit", KeywordToken)
986 | e.Limit.RenderTo(r)
987 | r.Control(NewLineToken)
988 | }
989 | if e.Offset != nil {
990 | r.Text("offset", KeywordToken)
991 | e.Offset.RenderTo(r)
992 | r.Control(NewLineToken)
993 | }
994 | }
995 |
996 | type AtTimeZoneExpr struct {
997 | Expr Expr
998 | TimeZone Expr
999 | }
1000 |
1001 | func (e AtTimeZoneExpr) RenderTo(r Renderer) {
1002 | e.Expr.RenderTo(r)
1003 | r.Text("at time zone", KeywordToken)
1004 | e.TimeZone.RenderTo(r)
1005 | }
1006 |
1007 | type LockingItem struct {
1008 | Strength string
1009 | LockedRels []AnyName
1010 | WaitPolicy string
1011 | }
1012 |
1013 | func (li LockingItem) RenderTo(r Renderer) {
1014 | r.Text("for", KeywordToken)
1015 | r.Text(li.Strength, KeywordToken)
1016 |
1017 | if li.LockedRels != nil {
1018 | r.Text("of", KeywordToken)
1019 |
1020 | for i, lr := range li.LockedRels {
1021 | lr.RenderTo(r)
1022 | if i < len(li.LockedRels)-1 {
1023 | r.Text(",", SymbolToken)
1024 | }
1025 | }
1026 | }
1027 |
1028 | if li.WaitPolicy != "" {
1029 | r.Text(li.WaitPolicy, KeywordToken)
1030 | }
1031 |
1032 | r.Control(NewLineToken)
1033 | }
1034 |
1035 | type LockingClause struct {
1036 | Locks []LockingItem
1037 | }
1038 |
1039 | func (lc LockingClause) RenderTo(r Renderer) {
1040 | for _, li := range lc.Locks {
1041 | li.RenderTo(r)
1042 | }
1043 | }
1044 |
1045 | type FuncExprNoParens string
1046 |
1047 | func (fe FuncExprNoParens) RenderTo(r Renderer) {
1048 | r.Text(string(fe), KeywordToken)
1049 | }
1050 |
1051 | type FuncExpr struct {
1052 | FuncApplication
1053 | WithinGroupClause *WithinGroupClause
1054 | FilterClause *FilterClause
1055 | OverClause *OverClause
1056 | }
1057 |
1058 | func (fe FuncExpr) RenderTo(r Renderer) {
1059 | fe.FuncApplication.RenderTo(r)
1060 |
1061 | if fe.WithinGroupClause != nil {
1062 | tr := &TokenRenderer{}
1063 | fe.WithinGroupClause.RenderTo(tr)
1064 | tokens := TryOneLine([]RenderToken(*tr), 60)
1065 | RenderTokens(r, tokens)
1066 | }
1067 |
1068 | if fe.FilterClause != nil {
1069 | fe.FilterClause.RenderTo(r)
1070 | }
1071 |
1072 | if fe.OverClause != nil {
1073 | fe.OverClause.RenderTo(r)
1074 | }
1075 | }
1076 |
1077 | type FuncApplication struct {
1078 | Name AnyName
1079 |
1080 | Distinct bool
1081 |
1082 | Star bool
1083 | Args []FuncArg
1084 | VariadicArg *FuncArg
1085 |
1086 | OrderClause *OrderClause
1087 | }
1088 |
1089 | func (fa FuncApplication) RenderTo(r Renderer) {
1090 | fa.Name.RenderTo(r)
1091 | r.Text("(", SymbolToken)
1092 |
1093 | if fa.Distinct {
1094 | r.Text("distinct", KeywordToken)
1095 | }
1096 |
1097 | if fa.Star {
1098 | r.Text("*", SymbolToken)
1099 | } else if len(fa.Args) > 0 {
1100 | for i, a := range fa.Args {
1101 | a.RenderTo(r)
1102 | if i < len(fa.Args)-1 {
1103 | r.Text(",", SymbolToken)
1104 | }
1105 | }
1106 | }
1107 |
1108 | if fa.VariadicArg != nil {
1109 | if len(fa.Args) > 0 {
1110 | r.Text(",", SymbolToken)
1111 | }
1112 |
1113 | r.Text("variadic", KeywordToken)
1114 | fa.VariadicArg.RenderTo(r)
1115 | }
1116 |
1117 | if fa.OrderClause != nil {
1118 | tr := &TokenRenderer{}
1119 | fa.OrderClause.RenderTo(tr)
1120 | tokens := TryOneLine([]RenderToken(*tr), 60)
1121 | RenderTokens(r, tokens)
1122 | }
1123 |
1124 | r.Text(")", SymbolToken)
1125 | }
1126 |
1127 | type FuncArg struct {
1128 | Name string
1129 | NameOp string
1130 | Expr Expr
1131 | }
1132 |
1133 | func (fa FuncArg) RenderTo(r Renderer) {
1134 | if fa.Name != "" {
1135 | r.Text(fa.Name, IdentifierToken)
1136 | r.Text(fa.NameOp, SymbolToken)
1137 | }
1138 | fa.Expr.RenderTo(r)
1139 | }
1140 |
1141 | type CastFunc struct {
1142 | Name string
1143 | Expr Expr
1144 | Type PgType
1145 | }
1146 |
1147 | func (cf CastFunc) RenderTo(r Renderer) {
1148 | r.Text(cf.Name, KeywordToken)
1149 | r.Text("(", SymbolToken)
1150 | cf.Expr.RenderTo(r)
1151 | r.Text("as", KeywordToken)
1152 | cf.Type.RenderTo(r)
1153 | r.Text(")", SymbolToken)
1154 | }
1155 |
1156 | type IsOfExpr struct {
1157 | Expr Expr
1158 | Not bool
1159 | Types []PgType
1160 | }
1161 |
1162 | func (io IsOfExpr) RenderTo(r Renderer) {
1163 | io.Expr.RenderTo(r)
1164 | r.Text("is", KeywordToken)
1165 |
1166 | if io.Not {
1167 | r.Text("not", KeywordToken)
1168 | }
1169 |
1170 | r.Text("of", KeywordToken)
1171 | r.Control(SpaceToken)
1172 | r.Text("(", SymbolToken)
1173 |
1174 | for i, t := range io.Types {
1175 | t.RenderTo(r)
1176 |
1177 | if i < len(io.Types)-1 {
1178 | r.Text(",", SymbolToken)
1179 | }
1180 | }
1181 |
1182 | r.Text(")", SymbolToken)
1183 | }
1184 |
1185 | type WithinGroupClause OrderClause
1186 |
1187 | func (w WithinGroupClause) RenderTo(r Renderer) {
1188 | r.Text("within group", KeywordToken)
1189 | r.Control(SpaceToken)
1190 | r.Text("(", SymbolToken)
1191 | OrderClause(w).RenderTo(r)
1192 | r.Text(")", SymbolToken)
1193 | }
1194 |
1195 | type FilterClause struct {
1196 | Expr
1197 | }
1198 |
1199 | func (f FilterClause) RenderTo(r Renderer) {
1200 | r.Text("filter", KeywordToken)
1201 | r.Control(SpaceToken)
1202 | r.Text("(", SymbolToken)
1203 | r.Text("where", KeywordToken)
1204 | f.Expr.RenderTo(r)
1205 | r.Text(")", SymbolToken)
1206 | }
1207 |
1208 | type DefaultExpr bool
1209 |
1210 | func (d DefaultExpr) RenderTo(r Renderer) {
1211 | r.Text("default", KeywordToken)
1212 | }
1213 |
1214 | type Row struct {
1215 | RowWord bool
1216 | Exprs []Expr
1217 | }
1218 |
1219 | func (row Row) RenderTo(r Renderer) {
1220 | if row.RowWord {
1221 | r.Text("row", KeywordToken)
1222 | }
1223 |
1224 | r.Text("(", SymbolToken)
1225 |
1226 | for i, e := range row.Exprs {
1227 | e.RenderTo(r)
1228 | if i < len(row.Exprs)-1 {
1229 | r.Text(",", SymbolToken)
1230 | }
1231 | }
1232 |
1233 | r.Text(")", SymbolToken)
1234 | }
1235 |
1236 | type ValuesRow []Expr
1237 |
1238 | func (vr ValuesRow) RenderTo(r Renderer) {
1239 | r.Text("(", SymbolToken)
1240 |
1241 | for i, e := range vr {
1242 | e.RenderTo(r)
1243 | if i < len(vr)-1 {
1244 | r.Text(",", SymbolToken)
1245 | }
1246 | }
1247 |
1248 | r.Text(")", SymbolToken)
1249 | }
1250 |
1251 | type ValuesClause []ValuesRow
1252 |
1253 | func (vc ValuesClause) RenderTo(r Renderer) {
1254 | r.Text("values", KeywordToken)
1255 | r.Control(NewLineToken)
1256 | r.Control(IndentToken)
1257 |
1258 | for i, row := range vc {
1259 | row.RenderTo(r)
1260 | if i < len(vc)-1 {
1261 | r.Text(",", SymbolToken)
1262 | }
1263 | r.Control(NewLineToken)
1264 | }
1265 |
1266 | r.Control(UnindentToken)
1267 | }
1268 |
1269 | type OverClause struct {
1270 | Name string
1271 | Specification *WindowSpecification
1272 | }
1273 |
1274 | func (oc *OverClause) RenderTo(r Renderer) {
1275 | r.Text("over", KeywordToken)
1276 | r.Control(SpaceToken)
1277 | if oc.Name != "" {
1278 | r.Text(oc.Name, IdentifierToken)
1279 | } else {
1280 | oc.Specification.RenderTo(r)
1281 | }
1282 | }
1283 |
1284 | type WindowClause []WindowDefinition
1285 |
1286 | func (wc WindowClause) RenderTo(r Renderer) {
1287 | r.Text("window", KeywordToken)
1288 | r.Control(NewLineToken)
1289 | r.Control(IndentToken)
1290 |
1291 | for i, wd := range wc {
1292 | wd.RenderTo(r)
1293 | if i < len(wc)-1 {
1294 | r.Text(",", SymbolToken)
1295 | }
1296 | r.Control(NewLineToken)
1297 | }
1298 |
1299 | r.Control(UnindentToken)
1300 | }
1301 |
1302 | type WindowDefinition struct {
1303 | Name string
1304 | Specification WindowSpecification
1305 | }
1306 |
1307 | func (wd WindowDefinition) RenderTo(r Renderer) {
1308 | r.Text(wd.Name, IdentifierToken)
1309 | r.Text("as", KeywordToken)
1310 | r.Control(SpaceToken)
1311 | wd.Specification.RenderTo(r)
1312 | }
1313 |
1314 | type WindowSpecification struct {
1315 | ExistingName string
1316 | PartitionClause PartitionClause
1317 | OrderClause *OrderClause
1318 | FrameClause *FrameClause
1319 | }
1320 |
1321 | func (ws WindowSpecification) RenderTo(r Renderer) {
1322 | r.Text("(", SymbolToken)
1323 |
1324 | if ws.ExistingName != "" {
1325 | r.Text(ws.ExistingName, IdentifierToken)
1326 | }
1327 |
1328 | if ws.PartitionClause != nil {
1329 | ws.PartitionClause.RenderTo(r)
1330 | }
1331 |
1332 | if ws.OrderClause != nil {
1333 | tr := &TokenRenderer{}
1334 | ws.OrderClause.RenderTo(tr)
1335 | tokens := TryOneLine([]RenderToken(*tr), 60)
1336 | RenderTokens(r, tokens)
1337 | }
1338 |
1339 | if ws.FrameClause != nil {
1340 | ws.FrameClause.RenderTo(r)
1341 | }
1342 |
1343 | r.Text(")", SymbolToken)
1344 | }
1345 |
1346 | type PartitionClause []Expr
1347 |
1348 | func (pc PartitionClause) RenderTo(r Renderer) {
1349 | r.Text("partition by", KeywordToken)
1350 |
1351 | for i, e := range pc {
1352 | e.RenderTo(r)
1353 | if i < len(pc)-1 {
1354 | r.Text(",", SymbolToken)
1355 | }
1356 | }
1357 | }
1358 |
1359 | type FrameClause struct {
1360 | Mode string
1361 | Start *FrameBound
1362 | End *FrameBound
1363 | }
1364 |
1365 | func (fc *FrameClause) RenderTo(r Renderer) {
1366 | r.Text(fc.Mode, KeywordToken)
1367 |
1368 | if fc.End != nil {
1369 | r.Text("between", KeywordToken)
1370 | fc.Start.RenderTo(r)
1371 | r.Text("and", KeywordToken)
1372 | fc.End.RenderTo(r)
1373 | } else {
1374 | fc.Start.RenderTo(r)
1375 | }
1376 | }
1377 |
1378 | type FrameBound struct {
1379 | CurrentRow bool
1380 |
1381 | BoundExpr Expr
1382 | Direction string
1383 | }
1384 |
1385 | func (fb FrameBound) RenderTo(r Renderer) {
1386 | if fb.CurrentRow {
1387 | r.Text("current row", KeywordToken)
1388 | return
1389 | }
1390 |
1391 | if fb.BoundExpr != nil {
1392 | fb.BoundExpr.RenderTo(r)
1393 | } else {
1394 | r.Text("unbounded", KeywordToken)
1395 | }
1396 |
1397 | r.Text(fb.Direction, KeywordToken)
1398 | }
1399 |
1400 | type RelationExpr struct {
1401 | Name AnyName
1402 | Star bool
1403 | Only bool
1404 | }
1405 |
1406 | func (re RelationExpr) RenderTo(r Renderer) {
1407 | if re.Only {
1408 | r.Text("only", KeywordToken)
1409 | }
1410 |
1411 | re.Name.RenderTo(r)
1412 |
1413 | if re.Star {
1414 | r.Text("*", SymbolToken)
1415 | }
1416 |
1417 | r.Control(NewLineToken)
1418 | }
1419 |
1420 | type SimpleSelect struct {
1421 | DistinctList []Expr
1422 | TargetList []Expr
1423 | IntoClause *IntoClause
1424 | FromClause *FromClause
1425 | WhereClause *WhereClause
1426 | GroupByClause *GroupByClause
1427 | HavingClause Expr
1428 | WindowClause WindowClause
1429 |
1430 | ValuesClause ValuesClause
1431 |
1432 | LeftSelect *SelectStmt
1433 | SetOp string
1434 | SetAll bool
1435 | RightSelect *SelectStmt
1436 |
1437 | Table *RelationExpr
1438 | }
1439 |
1440 | func (s SimpleSelect) RenderTo(r Renderer) {
1441 | if s.Table != nil {
1442 | r.Text("table", KeywordToken)
1443 | s.Table.RenderTo(r)
1444 | return
1445 | }
1446 |
1447 | if s.ValuesClause != nil {
1448 | s.ValuesClause.RenderTo(r)
1449 | return
1450 | }
1451 |
1452 | if s.LeftSelect != nil {
1453 | s.LeftSelect.RenderTo(r)
1454 | r.Control(NewLineToken)
1455 | r.Text(s.SetOp, KeywordToken)
1456 |
1457 | if s.SetAll {
1458 | r.Text("all", KeywordToken)
1459 | }
1460 |
1461 | r.Control(NewLineToken)
1462 |
1463 | s.RightSelect.RenderTo(r)
1464 |
1465 | return
1466 | }
1467 |
1468 | r.Text("select", KeywordToken)
1469 |
1470 | if s.DistinctList != nil {
1471 | r.Text("distinct", KeywordToken)
1472 |
1473 | if len(s.DistinctList) > 0 {
1474 | r.Text("on", KeywordToken)
1475 | r.Text("(", SymbolToken)
1476 |
1477 | for i, f := range s.DistinctList {
1478 | f.RenderTo(r)
1479 | if i < len(s.DistinctList)-1 {
1480 | r.Text(",", SymbolToken)
1481 | }
1482 | }
1483 | r.Text(")", SymbolToken)
1484 | }
1485 |
1486 | }
1487 |
1488 | r.Control(NewLineToken)
1489 | r.Control(IndentToken)
1490 | for i, f := range s.TargetList {
1491 | f.RenderTo(r)
1492 | if i < len(s.TargetList)-1 {
1493 | r.Text(",", SymbolToken)
1494 | }
1495 | r.Control(NewLineToken)
1496 | }
1497 | r.Control(UnindentToken)
1498 |
1499 | if s.IntoClause != nil {
1500 | s.IntoClause.RenderTo(r)
1501 | }
1502 |
1503 | if s.FromClause != nil {
1504 | s.FromClause.RenderTo(r)
1505 | }
1506 |
1507 | if s.WhereClause != nil {
1508 | s.WhereClause.RenderTo(r)
1509 | }
1510 |
1511 | if s.GroupByClause != nil {
1512 | s.GroupByClause.RenderTo(r)
1513 | }
1514 |
1515 | if s.HavingClause != nil {
1516 | r.Text("having", KeywordToken)
1517 | r.Control(NewLineToken)
1518 | r.Control(IndentToken)
1519 | s.HavingClause.RenderTo(r)
1520 | r.Control(NewLineToken)
1521 | }
1522 |
1523 | if s.WindowClause != nil {
1524 | s.WindowClause.RenderTo(r)
1525 | }
1526 | }
1527 |
1528 | type SelectStmt struct {
1529 | SimpleSelect
1530 | OrderClause *OrderClause
1531 | LimitClause *LimitClause
1532 | LockingClause *LockingClause
1533 |
1534 | ParenWrapped bool
1535 | Semicolon bool
1536 | }
1537 |
1538 | func (s SelectStmt) RenderTo(r Renderer) {
1539 | if s.ParenWrapped {
1540 | r.Text("(", SymbolToken)
1541 | }
1542 |
1543 | s.SimpleSelect.RenderTo(r)
1544 |
1545 | if s.OrderClause != nil {
1546 | s.OrderClause.RenderTo(r)
1547 | }
1548 |
1549 | if s.LimitClause != nil {
1550 | s.LimitClause.RenderTo(r)
1551 | }
1552 |
1553 | if s.LockingClause != nil {
1554 | s.LockingClause.RenderTo(r)
1555 | }
1556 |
1557 | if s.ParenWrapped {
1558 | r.Text(")", SymbolToken)
1559 | r.Control(NewLineToken)
1560 | }
1561 |
1562 | if s.Semicolon {
1563 | r.Text(";", SymbolToken)
1564 | r.Control(NewLineToken)
1565 | }
1566 | }
1567 |
1568 | type ExistsExpr SelectStmt
1569 |
1570 | func (e ExistsExpr) RenderTo(r Renderer) {
1571 | r.Text("exists", KeywordToken)
1572 |
1573 | SelectStmt(e).RenderTo(r)
1574 | }
1575 |
1576 | type ArraySubselect SelectStmt
1577 |
1578 | func (a ArraySubselect) RenderTo(r Renderer) {
1579 | r.Text("array", KeywordToken)
1580 |
1581 | SelectStmt(a).RenderTo(r)
1582 | }
1583 |
--------------------------------------------------------------------------------
/renderer.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | import (
4 | "io"
5 | "strings"
6 | )
7 |
8 | const (
9 | NullToken = iota
10 | KeywordToken = iota
11 | IdentifierToken = iota
12 | SymbolToken = iota
13 | ConstantToken = iota
14 | SpaceToken = iota
15 | RefuseSpaceToken = iota
16 | NewLineToken = iota
17 | IndentToken = iota
18 | UnindentToken = iota
19 | )
20 |
21 | type RenderToken struct {
22 | Type int
23 | Value string
24 | }
25 |
26 | type Renderer interface {
27 | Text(val string, typ int)
28 | Control(typ int)
29 | }
30 |
31 | type TextRenderer struct {
32 | w io.Writer
33 | err error
34 | indentLvl int
35 | indent string
36 | lineIndented bool
37 | newLine bool
38 | lastRenderToken RenderToken
39 |
40 | UpperCase bool
41 | }
42 |
43 | func (left RenderToken) SpaceBetween(right RenderToken) bool {
44 | if left.Type == RefuseSpaceToken {
45 | return false
46 | }
47 |
48 | switch left.Type {
49 | case KeywordToken, IdentifierToken, ConstantToken:
50 | switch right.Type {
51 | case KeywordToken, IdentifierToken, ConstantToken:
52 | return true
53 | case SymbolToken:
54 | switch right.Value {
55 | case "[", "(", "]", ")", ".", ",", "::", ":":
56 | return false
57 | }
58 | return true
59 | }
60 | case SymbolToken:
61 | switch left.Value {
62 | case ".", "(", "[", "::", ":":
63 | return false
64 | }
65 |
66 | if right.Type == NewLineToken {
67 | return false
68 | }
69 |
70 | if left.Value == "," {
71 | return true
72 | }
73 |
74 | if right.Type == SymbolToken {
75 | switch right.Value {
76 | case ".", "(", "[", "::", ")", "]", ",", ":":
77 | return false
78 | }
79 | }
80 |
81 | return true
82 | }
83 |
84 | return false
85 | }
86 |
87 | func NewTextRenderer(w io.Writer) *TextRenderer {
88 | return &TextRenderer{w: w, indent: " "}
89 | }
90 |
91 | func (tr *TextRenderer) Text(val string, tokenType int) {
92 | if !tr.lineIndented {
93 | for i := 0; i < tr.indentLvl; i++ {
94 | _, tr.err = io.WriteString(tr.w, tr.indent)
95 | if tr.err != nil {
96 | return
97 | }
98 | }
99 |
100 | tr.lineIndented = true
101 | }
102 |
103 | token := RenderToken{Type: tokenType, Value: val}
104 |
105 | if tr.newLine {
106 | tr.newLine = false
107 | } else if tr.lastRenderToken.SpaceBetween(token) {
108 | _, tr.err = io.WriteString(tr.w, " ")
109 | if tr.err != nil {
110 | return
111 | }
112 | }
113 |
114 | tr.lastRenderToken = token
115 |
116 | if tr.UpperCase {
117 | if tokenType == KeywordToken || tokenType == SymbolToken {
118 | val = strings.ToUpper(val)
119 | }
120 | }
121 |
122 | _, tr.err = io.WriteString(tr.w, val)
123 | }
124 |
125 | func (tr *TextRenderer) Control(typ int) {
126 | if tr.err != nil {
127 | return
128 | }
129 |
130 | switch typ {
131 | case SpaceToken:
132 | _, tr.err = io.WriteString(tr.w, " ")
133 | case NewLineToken:
134 | tr.renderNewLine()
135 | case IndentToken:
136 | tr.indentLvl = tr.indentLvl + 1
137 | case UnindentToken:
138 | tr.indentLvl = tr.indentLvl - 1
139 | }
140 |
141 | tr.lastRenderToken = RenderToken{Type: typ}
142 | }
143 |
144 | func (tr *TextRenderer) renderNewLine() {
145 | if tr.newLine {
146 | return
147 | }
148 |
149 | tr.newLine = true
150 | tr.lastRenderToken = RenderToken{Type: NewLineToken}
151 |
152 | _, tr.err = io.WriteString(tr.w, "\n")
153 | if tr.err != nil {
154 | return
155 | }
156 |
157 | tr.lineIndented = false
158 | }
159 |
160 | func (tr *TextRenderer) Error() error {
161 | return tr.err
162 | }
163 |
--------------------------------------------------------------------------------
/renderer_test.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestTextRenderer(t *testing.T) {
9 | var buf bytes.Buffer
10 |
11 | tr := NewTextRenderer(&buf)
12 |
13 | tr.Text("select", KeywordToken)
14 | tr.Control(NewLineToken)
15 | tr.Control(IndentToken)
16 | tr.Text("foo", IdentifierToken)
17 | tr.Text(",", SymbolToken)
18 | tr.Text("bar", IdentifierToken)
19 | tr.Control(NewLineToken)
20 | tr.Control(UnindentToken)
21 | tr.Text("from", KeywordToken)
22 | tr.Control(NewLineToken)
23 | tr.Control(IndentToken)
24 | tr.Text("baz", IdentifierToken)
25 |
26 | expected := `select
27 | foo, bar
28 | from
29 | baz`
30 |
31 | if buf.String() != expected {
32 | t.Errorf("Expected `%s`, got `%s`", expected, buf.String())
33 | }
34 | }
35 |
36 | func TestTextRendererUpper(t *testing.T) {
37 | var buf bytes.Buffer
38 |
39 | tr := NewTextRenderer(&buf)
40 | tr.UpperCase = true
41 |
42 | tr.Text("select", KeywordToken)
43 | tr.Control(NewLineToken)
44 | tr.Control(IndentToken)
45 | tr.Text("foo", IdentifierToken)
46 | tr.Text(",", SymbolToken)
47 | tr.Text("bar", IdentifierToken)
48 | tr.Control(NewLineToken)
49 | tr.Control(UnindentToken)
50 | tr.Text("from", KeywordToken)
51 | tr.Control(NewLineToken)
52 | tr.Control(IndentToken)
53 | tr.Text("baz", IdentifierToken)
54 | tr.Control(NewLineToken)
55 | tr.Control(UnindentToken)
56 | tr.Text("where", KeywordToken)
57 | tr.Control(NewLineToken)
58 | tr.Control(IndentToken)
59 | tr.Text("foo", IdentifierToken)
60 | tr.Text("=", KeywordToken)
61 | tr.Text("'foo'", ConstantToken)
62 |
63 | expected := `SELECT
64 | foo, bar
65 | FROM
66 | baz
67 | WHERE
68 | foo = 'foo'`
69 |
70 | if buf.String() != expected {
71 | t.Errorf("Expected `%s`, got `%s`", expected, buf.String())
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/testdata/arithmetic_expression.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 1 + 1,
3 | 2 - 1,
4 | 3 * 2,
5 | 8 / 2,
6 | 1 + 1 * 3,
7 | 3 + 8 / 7,
8 | 1 + 1 * 3,
9 | 312 + 8 / 7,
10 | 4 % 3,
11 | 7 ^ 5
12 |
--------------------------------------------------------------------------------
/testdata/arithmetic_expression.input.sql:
--------------------------------------------------------------------------------
1 | select 1 + 1, 2 - 1, 3 * 2, 8 / 2,
2 | 1 + 1 * 3, 3 + 8 / 7,
3 | 1+1*3, 312+8/7,
4 | 4%3, 7^5
5 |
--------------------------------------------------------------------------------
/testdata/array_constructor.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | array[],
3 | array[1],
4 | array[1, 2, 3, foo + bar],
5 | array[array[1, 2, 3], array[4, 5, 6]],
6 | array[[1, 2, 3], [4, 5, 6]]
7 | from
8 | baz
9 |
--------------------------------------------------------------------------------
/testdata/array_constructor.input.sql:
--------------------------------------------------------------------------------
1 | select array[], array[1], array[1,2,3,foo+bar], array[array[1,2,3], array[4,5,6]], array[[1,2,3], [4,5,6]] from baz
2 |
--------------------------------------------------------------------------------
/testdata/array_index.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | (array['a', 'b', 'c', foo, bar])[1],
3 | quz[42],
4 | (select
5 | array['a', 'b', 'c']
6 | )[1]
7 | from
8 | baz
9 |
--------------------------------------------------------------------------------
/testdata/array_index.input.sql:
--------------------------------------------------------------------------------
1 | select (array['a', 'b', 'c', foo, bar])[1], quz[42],
2 | (select array['a', 'b', 'c'])[1] from baz
3 |
--------------------------------------------------------------------------------
/testdata/array_slice.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | (array['a', 'b', 'c', foo, bar])[1:5],
3 | quz[42:50]
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/array_slice.input.sql:
--------------------------------------------------------------------------------
1 | select (array['a', 'b', 'c', foo, bar])[1:5], quz[42:50] from baz
2 |
--------------------------------------------------------------------------------
/testdata/array_subselect.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | array(select
4 | bar
5 | from
6 | quz
7 | where
8 | baz.foo = quz.foo
9 | )
10 | from
11 | baz
12 |
--------------------------------------------------------------------------------
/testdata/array_subselect.input.sql:
--------------------------------------------------------------------------------
1 | select foo, array(select bar from quz where baz.foo=quz.foo) from baz
2 |
--------------------------------------------------------------------------------
/testdata/array_typecast.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | '{1,2,3}'::int[],
3 | '{{1,2}, {3,4}}'::int[][],
4 | '{{1,2}, {3,4}}'::int[][2]
5 |
--------------------------------------------------------------------------------
/testdata/array_typecast.input.sql:
--------------------------------------------------------------------------------
1 | select '{1,2,3}'::int[], '{{1,2}, {3,4}}'::int[][], '{{1,2}, {3,4}}'::int[][2]
2 |
--------------------------------------------------------------------------------
/testdata/at_time_zone.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | '2015-01-01 00:00:00-09'::timestamptz at time zone 'America/Chicago'
3 |
--------------------------------------------------------------------------------
/testdata/at_time_zone.input.sql:
--------------------------------------------------------------------------------
1 | select '2015-01-01 00:00:00-09'::timestamptz at time zone 'America/Chicago'
2 |
--------------------------------------------------------------------------------
/testdata/b_expr.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo between bexpr::text and bar,
3 | foo between -42 and bar,
4 | foo between +3 and bar,
5 | foo between 1 + 1 and bar,
6 | foo between 1 - 1 and bar,
7 | foo between 1 * 1 and bar,
8 | foo between 1 / 1 and bar,
9 | foo between 1 % 1 and bar,
10 | foo between 1 ^ 1 and bar,
11 | foo between 1 < 1 and bar,
12 | foo between 1 > 1 and bar,
13 | foo between 1 = 1 and bar,
14 | foo between 1 <= 1 and bar,
15 | foo between 1 >= 1 and bar,
16 | foo between 1 != 1 and bar,
17 | foo between 1 @> 1 and bar,
18 | foo between @1 and bar,
19 | foo is distinct from bar,
20 | foo is not distinct from bar,
21 | true is of (integer, bool),
22 | 'asdf' is not of (integer, bool),
23 | foo between 5 ! and bar,
24 | false between foo is document and bar,
25 | false between foo is not document and bar
26 | from
27 | baz
28 |
--------------------------------------------------------------------------------
/testdata/b_expr.input.sql:
--------------------------------------------------------------------------------
1 | select foo between bexpr::text and bar,
2 | foo between -42 and bar,
3 | foo between +3 and bar,
4 | foo between 1+1 and bar,
5 | foo between 1-1 and bar,
6 | foo between 1*1 and bar,
7 | foo between 1/1 and bar,
8 | foo between 1%1 and bar,
9 | foo between 1^1 and bar,
10 | foo between 1<1 and bar,
11 | foo between 1>1 and bar,
12 | foo between 1=1 and bar,
13 | foo between 1<=1 and bar,
14 | foo between 1>=1 and bar,
15 | foo between 1!=1 and bar,
16 | foo between 1@>1 and bar,
17 | foo between @1 and bar,
18 | foo is distinct from bar,
19 | foo is not distinct from bar,
20 | true is of (integer, bool),
21 | 'asdf' is not of (integer, bool),
22 | foo between 5! and bar,
23 | false between foo is document and bar,
24 | false between foo is not document and bar
25 |
26 |
27 | from baz
28 |
--------------------------------------------------------------------------------
/testdata/between.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo between bar and baz,
3 | foo not between bar and baz,
4 | foo between bar and baz,
5 | foo not between bar and baz,
6 | foo between symmetric bar and baz,
7 | foo not between symmetric bar and baz
8 |
--------------------------------------------------------------------------------
/testdata/between.input.sql:
--------------------------------------------------------------------------------
1 | select foo between bar and baz, foo not between bar and baz,
2 | foo between asymmetric bar and baz, foo not between asymmetric bar and baz,
3 | foo between symmetric bar and baz, foo not between symmetric bar and baz
4 |
--------------------------------------------------------------------------------
/testdata/bitconst.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | b'10101',
3 | x'0123456789abcdefABCDEF'
4 |
--------------------------------------------------------------------------------
/testdata/bitconst.input.sql:
--------------------------------------------------------------------------------
1 | select b'10101',x'0123456789abcdefABCDEF'
2 |
--------------------------------------------------------------------------------
/testdata/boolean_binary_op.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo
3 | and bar,
4 | baz
5 | or quz
6 | from
7 | t
8 |
--------------------------------------------------------------------------------
/testdata/boolean_binary_op.input.sql:
--------------------------------------------------------------------------------
1 | select foo and bar, baz or quz from t
2 |
--------------------------------------------------------------------------------
/testdata/boolean_not.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | not foo,
3 | not true,
4 | not false
5 | from
6 | t
7 |
--------------------------------------------------------------------------------
/testdata/boolean_not.input.sql:
--------------------------------------------------------------------------------
1 | select not foo, not true, not false from t
2 |
--------------------------------------------------------------------------------
/testdata/case_full.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | case
3 | when foo = bar then
4 | 7
5 | when foo > bar then
6 | 42
7 | else
8 | 1
9 | end
10 | from
11 | baz
12 |
--------------------------------------------------------------------------------
/testdata/case_full.input.sql:
--------------------------------------------------------------------------------
1 | select case when foo=bar then 7 when foo>bar then 42 else 1 end from baz
2 |
--------------------------------------------------------------------------------
/testdata/case_implicit.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | case foo
3 | when 4 then
4 | 'A'
5 | when 3 then
6 | 'B'
7 | else
8 | 'C'
9 | end
10 | from
11 | baz
12 |
--------------------------------------------------------------------------------
/testdata/case_implicit.input.sql:
--------------------------------------------------------------------------------
1 | select case foo when 4 then 'A' when 3 then 'B' else 'C' end from baz
2 |
--------------------------------------------------------------------------------
/testdata/cast_as.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | cast('{1,2,3}' as int[])
3 |
--------------------------------------------------------------------------------
/testdata/cast_as.input.sql:
--------------------------------------------------------------------------------
1 | select cast('{1,2,3}' as int[])
2 |
--------------------------------------------------------------------------------
/testdata/collate.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 'Foo' collate "C",
3 | 'Bar' collate "en_US"
4 |
--------------------------------------------------------------------------------
/testdata/collate.input.sql:
--------------------------------------------------------------------------------
1 | select 'Foo' collate "C", 'Bar' collate "en_US"
2 |
--------------------------------------------------------------------------------
/testdata/collation_for.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | collation for(name)
3 | from
4 | people
5 |
--------------------------------------------------------------------------------
/testdata/collation_for.input.sql:
--------------------------------------------------------------------------------
1 | select collation for (name) from people
2 |
--------------------------------------------------------------------------------
/testdata/comment_beginning_single_line.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/comment_beginning_single_line.input.sql:
--------------------------------------------------------------------------------
1 | -- TODO - do not strip comments
2 | select foo, bar from baz
3 |
--------------------------------------------------------------------------------
/testdata/comparison_expression.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 1 = 1,
3 | 2 > 1,
4 | 2 < 8,
5 | 1 != 2,
6 | 1 != 2,
7 | 3 >= 2,
8 | 2 <= 7
9 |
--------------------------------------------------------------------------------
/testdata/comparison_expression.input.sql:
--------------------------------------------------------------------------------
1 | select 1 = 1, 2 > 1, 2 < 8, 1!=2, 1<>2, 3>=2, 2 <= 7
2 |
--------------------------------------------------------------------------------
/testdata/const_type_name.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | char 'hi',
3 | char(2) 'hi',
4 | varchar 'hi',
5 | varchar(2) 'hi',
6 | bit '1010',
7 | bit(4) '1010',
8 | varbit '1010',
9 | varbit(4) '1010',
10 | timestamp(4) '2000-01-01 00:00:00',
11 | timestamp(4) with time zone '2000-01-01 00:00:00',
12 | timestamp(4) '2000-01-01 00:00:00',
13 | timestamp '2000-01-01 00:00:00',
14 | timestamp with time zone '2000-01-01 00:00:00',
15 | timestamp '2000-01-01 00:00:00',
16 | time(4) '00:00:00',
17 | time(4) with time zone '00:00:00',
18 | time(4) '00:00:00',
19 | time '00:00:00',
20 | time with time zone '00:00:00',
21 | time '00:00:00'
22 |
--------------------------------------------------------------------------------
/testdata/const_type_name.input.sql:
--------------------------------------------------------------------------------
1 | select char 'hi', char(2) 'hi', varchar 'hi', varchar(2) 'hi',
2 | bit '1010', bit(4) '1010', varbit '1010', varbit(4) '1010',
3 | timestamp(4) '2000-01-01 00:00:00', timestamp(4) with time zone '2000-01-01 00:00:00', timestamp(4) without time zone '2000-01-01 00:00:00',
4 | timestamp '2000-01-01 00:00:00', timestamp with time zone '2000-01-01 00:00:00', timestamp without time zone '2000-01-01 00:00:00',
5 | time(4) '00:00:00', time(4) with time zone '00:00:00', time(4) without time zone '00:00:00',
6 | time '00:00:00', time with time zone '00:00:00', time without time zone '00:00:00'
7 |
--------------------------------------------------------------------------------
/testdata/custom_operators.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo @> bar,
3 | @foo,
4 | 'foo' || 'bar'
5 |
--------------------------------------------------------------------------------
/testdata/custom_operators.input.sql:
--------------------------------------------------------------------------------
1 | select foo @> bar, @foo, 'foo' || 'bar'
2 |
--------------------------------------------------------------------------------
/testdata/distinct.golden.sql:
--------------------------------------------------------------------------------
1 | select distinct
2 | foo,
3 | bar
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/distinct.input.sql:
--------------------------------------------------------------------------------
1 | select distinct foo, bar from baz
2 |
--------------------------------------------------------------------------------
/testdata/distinct_on.golden.sql:
--------------------------------------------------------------------------------
1 | select distinct on(foo)
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | foo
8 |
--------------------------------------------------------------------------------
/testdata/distinct_on.input.sql:
--------------------------------------------------------------------------------
1 | select distinct on (foo) foo, bar from baz order by foo
2 |
--------------------------------------------------------------------------------
/testdata/except.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | except
7 | select
8 | a,
9 | b
10 | from
11 | quz
12 |
--------------------------------------------------------------------------------
/testdata/except.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz except select a, b from quz
2 |
--------------------------------------------------------------------------------
/testdata/exists.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | where
7 | exists(select
8 | 1
9 | from
10 | quz
11 | )
12 |
--------------------------------------------------------------------------------
/testdata/exists.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz where exists(select 1 from quz)
2 |
--------------------------------------------------------------------------------
/testdata/extract.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | extract(year from '2000-01-01 12:34:56'::timestamptz),
3 | extract(month from '2000-01-01 12:34:56'::timestamptz),
4 | extract(day from '2000-01-01 12:34:56'::timestamptz),
5 | extract(hour from '2000-01-01 12:34:56'::timestamptz),
6 | extract(minute from '2000-01-01 12:34:56'::timestamptz),
7 | extract(second from '2000-01-01 12:34:56'::timestamptz),
8 | extract('second' from '2000-01-01 12:34:56'::timestamptz),
9 | extract("second" from '2000-01-01 12:34:56'::timestamptz)
10 |
--------------------------------------------------------------------------------
/testdata/extract.input.sql:
--------------------------------------------------------------------------------
1 | select extract(year from '2000-01-01 12:34:56'::timestamptz),
2 | extract(month from '2000-01-01 12:34:56'::timestamptz),
3 | extract(day from '2000-01-01 12:34:56'::timestamptz),
4 | extract(hour from '2000-01-01 12:34:56'::timestamptz),
5 | extract(minute from '2000-01-01 12:34:56'::timestamptz),
6 | extract(second from '2000-01-01 12:34:56'::timestamptz),
7 | extract('second' from '2000-01-01 12:34:56'::timestamptz),
8 | extract("second" from '2000-01-01 12:34:56'::timestamptz)
9 |
--------------------------------------------------------------------------------
/testdata/float_constant.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 3.14
3 |
--------------------------------------------------------------------------------
/testdata/float_constant.input.sql:
--------------------------------------------------------------------------------
1 | select 3.14
2 |
--------------------------------------------------------------------------------
/testdata/func_expr_expr_list.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | coalesce(a, b, c),
3 | greatest(d, e, f),
4 | least(g, h, i),
5 | xmlconcat(j, k, l)
6 | from
7 | foo
8 |
--------------------------------------------------------------------------------
/testdata/func_expr_expr_list.input.sql:
--------------------------------------------------------------------------------
1 | select coalesce(a,b,c), greatest(d,e,f), least(g,h,i), xmlconcat(j,k,l) from foo
2 |
--------------------------------------------------------------------------------
/testdata/func_expr_no_parens.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | current_date,
3 | current_time,
4 | current_timestamp,
5 | localtime,
6 | localtimestamp,
7 | current_role,
8 | current_user,
9 | session_user,
10 | user,
11 | current_catalog,
12 | current_schema
13 |
--------------------------------------------------------------------------------
/testdata/func_expr_no_parens.input.sql:
--------------------------------------------------------------------------------
1 | select current_date, current_time, current_timestamp,
2 | localtime, localtimestamp, current_role, current_user,
3 | session_user, user, current_catalog, current_schema
4 |
--------------------------------------------------------------------------------
/testdata/func_expr_one_arg.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | current_time(2),
3 | current_timestamp(2),
4 | localtime(2),
5 | localtimestamp(2)
6 |
--------------------------------------------------------------------------------
/testdata/func_expr_one_arg.input.sql:
--------------------------------------------------------------------------------
1 | select current_time(2), current_timestamp(2),
2 | localtime(2), localtimestamp(2)
3 |
--------------------------------------------------------------------------------
/testdata/function_call_qualified.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo.quz(bar)
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/function_call_qualified.input.sql:
--------------------------------------------------------------------------------
1 | select foo.quz(bar) from baz
2 |
--------------------------------------------------------------------------------
/testdata/function_call_variadic.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo(variadic array[1, 2, 3]),
3 | bar(1, 2, variadic array[3, 4, 5])
4 |
--------------------------------------------------------------------------------
/testdata/function_call_variadic.input.sql:
--------------------------------------------------------------------------------
1 | select foo(variadic array[1,2,3]), bar(1, 2, variadic array[3,4,5])
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_all.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | name,
3 | array_agg(foo)
4 | from
5 | baz
6 | group by
7 | name
8 |
--------------------------------------------------------------------------------
/testdata/function_call_with_all.input.sql:
--------------------------------------------------------------------------------
1 | select name, array_agg(all foo) from baz group by name
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_distinct.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | name,
3 | array_agg(distinct foo)
4 | from
5 | baz
6 | group by
7 | name
8 |
--------------------------------------------------------------------------------
/testdata/function_call_with_distinct.input.sql:
--------------------------------------------------------------------------------
1 | select name, array_agg(distinct foo) from baz group by name
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_filter.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | name,
3 | array_agg(foo) filter (where a = b)
4 | from
5 | baz
6 | group by
7 | name
8 |
--------------------------------------------------------------------------------
/testdata/function_call_with_filter.input.sql:
--------------------------------------------------------------------------------
1 | select name, array_agg(foo) filter (where a=b) from baz group by name
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_grouping_set.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | percentile_disc(0.25) within group (order by n)
3 | from
4 | generate_series(1, 10) as n
5 |
--------------------------------------------------------------------------------
/testdata/function_call_with_grouping_set.input.sql:
--------------------------------------------------------------------------------
1 | select percentile_disc(0.25) within group (order by n) from generate_series(1,10) n
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_order.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | name,
3 | array_agg(foo order by bar)
4 | from
5 | baz
6 | group by
7 | name
8 |
--------------------------------------------------------------------------------
/testdata/function_call_with_order.input.sql:
--------------------------------------------------------------------------------
1 | select name, array_agg(foo order by bar) from baz group by name
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_pg_named_args.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | quz(foo := 1, bar := 2)
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/function_call_with_pg_named_args.input.sql:
--------------------------------------------------------------------------------
1 | select quz(foo:=1,bar:=2) from baz
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_positional_args.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | quz(foo, bar)
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/function_call_with_positional_args.input.sql:
--------------------------------------------------------------------------------
1 | select quz(foo,bar) from baz
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_sql_named_args.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | quz(foo => 1, bar => 2)
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/function_call_with_sql_named_args.input.sql:
--------------------------------------------------------------------------------
1 | select quz(foo=>1,bar=>2) from baz
2 |
--------------------------------------------------------------------------------
/testdata/function_call_with_star_arg.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | count(*)
4 | from
5 | bar
6 | group by
7 | foo
8 |
--------------------------------------------------------------------------------
/testdata/function_call_with_star_arg.input.sql:
--------------------------------------------------------------------------------
1 | select foo, count(*) from bar group by foo
2 |
--------------------------------------------------------------------------------
/testdata/function_call_without_args.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | now()
3 |
--------------------------------------------------------------------------------
/testdata/function_call_without_args.input.sql:
--------------------------------------------------------------------------------
1 | select now()
2 |
--------------------------------------------------------------------------------
/testdata/group_by.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | group by
7 | foo,
8 | bar
9 |
--------------------------------------------------------------------------------
/testdata/group_by.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz group by foo, bar
2 |
--------------------------------------------------------------------------------
/testdata/having.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | group by
7 | foo,
8 | bar
9 | having
10 | foo > 42
11 |
--------------------------------------------------------------------------------
/testdata/having.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz group by foo, bar having foo > 42
2 |
--------------------------------------------------------------------------------
/testdata/in.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 2 in (1, 2, 3),
3 | 2 not in (1, 2, 3),
4 | 2 in (select
5 | generate_series(1, 10)
6 | )
7 | ,
8 | 2 not in (select
9 | generate_series(1, 10)
10 | )
11 |
--------------------------------------------------------------------------------
/testdata/in.input.sql:
--------------------------------------------------------------------------------
1 | select 2 in (1,2,3), 2 not in (1,2,3),
2 | 2 in (select generate_series(1,10)), 2 not in (select generate_series(1,10))
3 |
--------------------------------------------------------------------------------
/testdata/intersect.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | intersect
7 | select
8 | a,
9 | b
10 | from
11 | quz
12 |
--------------------------------------------------------------------------------
/testdata/intersect.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz intersect select a, b from quz
2 |
--------------------------------------------------------------------------------
/testdata/interval.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | interval '5',
3 | interval '5' hour,
4 | interval '5' hour to minute,
5 | interval '5' second(5),
6 | interval(2) '10.324'
7 |
--------------------------------------------------------------------------------
/testdata/interval.input.sql:
--------------------------------------------------------------------------------
1 | select interval '5', interval '5' hour, interval '5' hour to minute, interval '5' second(5),
2 | interval(2) '10.324'
3 |
--------------------------------------------------------------------------------
/testdata/is_bool_op.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo is true,
3 | foo is not true,
4 | foo is false,
5 | foo is not false,
6 | foo is unknown,
7 | foo is not unknown
8 | from
9 | bar
10 |
--------------------------------------------------------------------------------
/testdata/is_bool_op.input.sql:
--------------------------------------------------------------------------------
1 | select foo is true, foo is not true, foo is false, foo is not false, foo is unknown, foo is not unknown from bar
2 |
--------------------------------------------------------------------------------
/testdata/is_distinct_from.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo is distinct from bar,
3 | foo is not distinct from bar
4 | from
5 | bar
6 |
--------------------------------------------------------------------------------
/testdata/is_distinct_from.input.sql:
--------------------------------------------------------------------------------
1 | select foo is distinct from bar, foo is not distinct from bar from bar
2 |
--------------------------------------------------------------------------------
/testdata/is_document.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo is document,
3 | foo is not document
4 | from
5 | bar
6 |
--------------------------------------------------------------------------------
/testdata/is_document.input.sql:
--------------------------------------------------------------------------------
1 | select foo is document, foo is not document from bar
2 |
--------------------------------------------------------------------------------
/testdata/is_null.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo is null,
3 | foo is not null,
4 | foo is null,
5 | foo is not null
6 | from
7 | bar
8 |
--------------------------------------------------------------------------------
/testdata/is_null.input.sql:
--------------------------------------------------------------------------------
1 | select foo is null, foo is not null, foo isnull, foo notnull from bar
2 |
--------------------------------------------------------------------------------
/testdata/is_of_type_list.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | true is of (integer, bool),
3 | 'asdf' is not of (integer, bool)
4 |
--------------------------------------------------------------------------------
/testdata/is_of_type_list.input.sql:
--------------------------------------------------------------------------------
1 | select true is of (integer, bool), 'asdf' is not of (integer, bool)
2 |
--------------------------------------------------------------------------------
/testdata/like.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | where
7 | foo like 'abd%'
8 | or foo like 'ada%' escape '!'
9 | or foo not like 'abd%'
10 | or foo not like 'ada%' escape '!'
11 | or foo ilike 'efg%'
12 | or foo ilike 'ada%' escape '!'
13 | or foo not ilike 'efg%'
14 | or foo not ilike 'ada%' escape '!'
15 |
--------------------------------------------------------------------------------
/testdata/like.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz
2 | where
3 | foo like 'abd%' or foo like 'ada%' escape '!' or
4 | foo not like 'abd%' or foo not like 'ada%' escape '!'
5 | or foo ilike 'efg%' or foo ilike 'ada%' escape '!'
6 | or foo not ilike 'efg%' or foo not ilike 'ada%' escape '!'
7 |
--------------------------------------------------------------------------------
/testdata/limit.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | limit 42
7 |
--------------------------------------------------------------------------------
/testdata/limit.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz limit 42
2 |
--------------------------------------------------------------------------------
/testdata/limit_fetch.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | limit 42
7 |
--------------------------------------------------------------------------------
/testdata/limit_fetch.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz fetch first 42 rows only
2 |
--------------------------------------------------------------------------------
/testdata/limit_offset.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | limit 7
7 | offset 42
8 |
--------------------------------------------------------------------------------
/testdata/limit_offset.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz limit 7 offset 42
2 |
--------------------------------------------------------------------------------
/testdata/null.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | null
3 |
--------------------------------------------------------------------------------
/testdata/null.input.sql:
--------------------------------------------------------------------------------
1 | select null
2 |
--------------------------------------------------------------------------------
/testdata/nullif.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | nullif(1, 2)
3 |
--------------------------------------------------------------------------------
/testdata/nullif.input.sql:
--------------------------------------------------------------------------------
1 | select nullif(1,2)
2 |
--------------------------------------------------------------------------------
/testdata/offset.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | offset 42
7 |
--------------------------------------------------------------------------------
/testdata/offset.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz offset 42
2 |
--------------------------------------------------------------------------------
/testdata/offset_fetch.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | limit 7
7 | offset 42
8 |
--------------------------------------------------------------------------------
/testdata/offset_fetch.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz offset 42 rows fetch next 7 rows only
2 |
--------------------------------------------------------------------------------
/testdata/offset_limit.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | limit 7
7 | offset 42
8 |
--------------------------------------------------------------------------------
/testdata/offset_limit.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz offset 42 limit 7
2 |
--------------------------------------------------------------------------------
/testdata/order.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | quz
8 |
--------------------------------------------------------------------------------
/testdata/order.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by quz
2 |
--------------------------------------------------------------------------------
/testdata/order_column_num.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | 1
8 |
--------------------------------------------------------------------------------
/testdata/order_column_num.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by 1
2 |
--------------------------------------------------------------------------------
/testdata/order_desc.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | quz desc
8 |
--------------------------------------------------------------------------------
/testdata/order_desc.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by quz desc
2 |
--------------------------------------------------------------------------------
/testdata/order_multiple.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | foo desc,
8 | quz asc
9 |
--------------------------------------------------------------------------------
/testdata/order_multiple.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by foo desc, quz asc
2 |
--------------------------------------------------------------------------------
/testdata/order_nulls.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | foo desc nulls first,
8 | quz asc nulls last,
9 | abc nulls last
10 |
--------------------------------------------------------------------------------
/testdata/order_nulls.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by foo desc nulls first, quz asc nulls last, abc nulls last
2 |
--------------------------------------------------------------------------------
/testdata/order_using.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | order by
7 | quz using <
8 |
--------------------------------------------------------------------------------
/testdata/order_using.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz order by quz using <
2 |
--------------------------------------------------------------------------------
/testdata/overlaps.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | (date '2000-01-01', date '2000-01-31') overlaps (date '2000-01-15', date '2000-02-15')
3 |
--------------------------------------------------------------------------------
/testdata/overlaps.input.sql:
--------------------------------------------------------------------------------
1 | select (date '2000-01-01', date '2000-01-31') overlaps (date '2000-01-15', date '2000-02-15')
2 |
--------------------------------------------------------------------------------
/testdata/overlay.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | overlay('Taaas' placing 'ex' from 2 for 2),
3 | overlay('Taaas' placing 'ex' from 2)
4 |
--------------------------------------------------------------------------------
/testdata/overlay.input.sql:
--------------------------------------------------------------------------------
1 | select overlay('Taaas' placing 'ex' from 2 for 2),
2 | overlay('Taaas' placing 'ex' from 2)
3 |
--------------------------------------------------------------------------------
/testdata/paren_expression.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | (1 + 3) * 4
3 |
--------------------------------------------------------------------------------
/testdata/paren_expression.input.sql:
--------------------------------------------------------------------------------
1 | select (1 + 3)*4
2 |
--------------------------------------------------------------------------------
/testdata/position.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | position('og' in 'groggy')
3 |
--------------------------------------------------------------------------------
/testdata/position.input.sql:
--------------------------------------------------------------------------------
1 | select position('og' in 'groggy')
2 |
--------------------------------------------------------------------------------
/testdata/postfix_operator.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 5 !,
3 | 7 !
4 |
--------------------------------------------------------------------------------
/testdata/postfix_operator.input.sql:
--------------------------------------------------------------------------------
1 | select 5 !, 7!
2 |
--------------------------------------------------------------------------------
/testdata/quoted_identifier.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | "Foo Bar",
3 | "Embedded "" Quote"
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/quoted_identifier.input.sql:
--------------------------------------------------------------------------------
1 | select "Foo Bar", "Embedded "" Quote" from baz
2 |
--------------------------------------------------------------------------------
/testdata/row.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | row(),
3 | row(1),
4 | row(1, 2),
5 | (1, 2, 3)
6 |
--------------------------------------------------------------------------------
/testdata/row.input.sql:
--------------------------------------------------------------------------------
1 | select row (), row (1), row (1,2), (1,2,3)
2 |
--------------------------------------------------------------------------------
/testdata/select_for_key_share.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for key share
7 |
--------------------------------------------------------------------------------
/testdata/select_for_key_share.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for key share
2 |
--------------------------------------------------------------------------------
/testdata/select_for_no_key_update.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for no key update
7 |
--------------------------------------------------------------------------------
/testdata/select_for_no_key_update.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for no key update
2 |
--------------------------------------------------------------------------------
/testdata/select_for_share.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for share
7 |
--------------------------------------------------------------------------------
/testdata/select_for_share.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for share
2 |
--------------------------------------------------------------------------------
/testdata/select_for_update.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for update
7 |
--------------------------------------------------------------------------------
/testdata/select_for_update.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for update
2 |
--------------------------------------------------------------------------------
/testdata/select_for_update_nowait.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for update nowait
7 |
--------------------------------------------------------------------------------
/testdata/select_for_update_nowait.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for update nowait
2 |
--------------------------------------------------------------------------------
/testdata/select_for_update_of.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | for update of baz
7 |
--------------------------------------------------------------------------------
/testdata/select_for_update_of.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz for update of baz
2 |
--------------------------------------------------------------------------------
/testdata/select_from_aliased.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | quz.foo,
3 | quz.bar
4 | from
5 | baz as quz
6 |
--------------------------------------------------------------------------------
/testdata/select_from_aliased.input.sql:
--------------------------------------------------------------------------------
1 | select quz.foo, quz.bar from baz as quz
2 |
--------------------------------------------------------------------------------
/testdata/select_from_comma_join.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz,
6 | quz
7 |
--------------------------------------------------------------------------------
/testdata/select_from_comma_join.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz, quz
3 |
--------------------------------------------------------------------------------
/testdata/select_from_cross_join.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | cross join quz
7 |
--------------------------------------------------------------------------------
/testdata/select_from_cross_join.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz cross join quz
3 |
--------------------------------------------------------------------------------
/testdata/select_from_join_on.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | join quz on baz.a = quz.b
7 |
--------------------------------------------------------------------------------
/testdata/select_from_join_on.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz join quz on baz.a = quz.b
3 |
--------------------------------------------------------------------------------
/testdata/select_from_join_using.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | join quz using(id)
7 |
--------------------------------------------------------------------------------
/testdata/select_from_join_using.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz join quz using(id)
3 |
--------------------------------------------------------------------------------
/testdata/select_from_join_using_multiple.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | join quz using(foo, bar)
7 |
--------------------------------------------------------------------------------
/testdata/select_from_join_using_multiple.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz join quz using(foo, bar)
3 |
--------------------------------------------------------------------------------
/testdata/select_from_left_join_on.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | left join quz on baz.a = quz.b
7 |
--------------------------------------------------------------------------------
/testdata/select_from_left_join_on.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz left join quz on baz.a = quz.b
3 |
--------------------------------------------------------------------------------
/testdata/select_from_natural_join.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | natural join quz
7 |
--------------------------------------------------------------------------------
/testdata/select_from_natural_join.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 | from baz natural join quz
3 |
--------------------------------------------------------------------------------
/testdata/select_into.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | into quz
5 | from
6 | baz
7 |
--------------------------------------------------------------------------------
/testdata/select_into.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar into quz from baz
2 |
--------------------------------------------------------------------------------
/testdata/select_star.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | *
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/select_star.input.sql:
--------------------------------------------------------------------------------
1 | select * from baz
2 |
--------------------------------------------------------------------------------
/testdata/select_table_dot_column.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | baz.foo,
3 | baz.bar as quz
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/select_table_dot_column.input.sql:
--------------------------------------------------------------------------------
1 | select baz.foo, baz.bar as quz from baz
2 |
--------------------------------------------------------------------------------
/testdata/select_table_dot_star.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | baz.*
3 | from
4 | baz
5 |
--------------------------------------------------------------------------------
/testdata/select_table_dot_star.input.sql:
--------------------------------------------------------------------------------
1 | select baz.* from baz
2 |
--------------------------------------------------------------------------------
/testdata/select_where.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | where
7 | foo > 5
8 | and bar < 2
9 |
--------------------------------------------------------------------------------
/testdata/select_where.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz where foo > 5 and bar < 2
2 |
--------------------------------------------------------------------------------
/testdata/select_wrapped_by_parens.golden.sql:
--------------------------------------------------------------------------------
1 | (select
2 | foo
3 | from
4 | bar
5 | )
6 |
--------------------------------------------------------------------------------
/testdata/select_wrapped_by_parens.input.sql:
--------------------------------------------------------------------------------
1 | (select foo from bar)
2 |
--------------------------------------------------------------------------------
/testdata/semicolon.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo
3 | from
4 | bar
5 | ;
6 |
--------------------------------------------------------------------------------
/testdata/semicolon.input.sql:
--------------------------------------------------------------------------------
1 | select foo from bar;
2 |
--------------------------------------------------------------------------------
/testdata/simple_select_literal_integer.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 42
3 |
--------------------------------------------------------------------------------
/testdata/simple_select_literal_integer.input.sql:
--------------------------------------------------------------------------------
1 | select 42
2 |
--------------------------------------------------------------------------------
/testdata/simple_select_literal_text.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 'foo',
3 | 'bar' as quz,
4 | 'It''s'
5 |
--------------------------------------------------------------------------------
/testdata/simple_select_literal_text.input.sql:
--------------------------------------------------------------------------------
1 | select 'foo', 'bar' as quz, 'It''s'
2 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_from.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_from.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz
2 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_selection_alias.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo as f,
3 | bar as b
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_selection_alias.input.sql:
--------------------------------------------------------------------------------
1 | select foo as f, bar as b
2 | from baz
3 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_selection_alias_no_as.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo as f,
3 | bar as b
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/simple_select_with_selection_alias_no_as.input.sql:
--------------------------------------------------------------------------------
1 | select foo f, bar b
2 | from baz
3 |
--------------------------------------------------------------------------------
/testdata/simple_select_without_from.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 |
--------------------------------------------------------------------------------
/testdata/simple_select_without_from.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar
2 |
--------------------------------------------------------------------------------
/testdata/subquery_op.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | 3 > any (select
3 | generate_series(1, 10)
4 | )
5 | ,
6 | 3 > all (select
7 | generate_series(1, 10)
8 | )
9 | ,
10 | 3 > any (array[1, 2, 3, 4]),
11 | 3 operator(>) any (array[1, 2, 3, 4])
12 |
--------------------------------------------------------------------------------
/testdata/subquery_op.input.sql:
--------------------------------------------------------------------------------
1 | select 3 > any (select generate_series(1,10)),
2 | 3 > all (select generate_series(1,10)),
3 | 3 > any (array[1,2,3,4]),
4 | 3 operator(>) any (array[1,2,3,4])
5 |
--------------------------------------------------------------------------------
/testdata/subselect_expression.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | (select
3 | 1
4 | from
5 | foo
6 | )
7 |
--------------------------------------------------------------------------------
/testdata/subselect_expression.input.sql:
--------------------------------------------------------------------------------
1 | select (select 1 from foo)
2 |
--------------------------------------------------------------------------------
/testdata/substring.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | substring('Thomas' from 2 for 3),
3 | substring('Thomas' from '...$'),
4 | substring('Thomas' from '%#"o_a#"_' for '#'),
5 | substring('Thomas', 2, 3),
6 | substring()
7 |
--------------------------------------------------------------------------------
/testdata/substring.input.sql:
--------------------------------------------------------------------------------
1 | select substring('Thomas' from 2 for 3), substring('Thomas' from '...$'),
2 | substring('Thomas' from '%#"o_a#"_' for '#'), substring('Thomas', 2, 3),
3 | substring()
4 |
--------------------------------------------------------------------------------
/testdata/table.golden.sql:
--------------------------------------------------------------------------------
1 | table baz
2 |
--------------------------------------------------------------------------------
/testdata/table.input.sql:
--------------------------------------------------------------------------------
1 | table baz
2 |
--------------------------------------------------------------------------------
/testdata/table_only.golden.sql:
--------------------------------------------------------------------------------
1 | table only baz
2 |
--------------------------------------------------------------------------------
/testdata/table_only.input.sql:
--------------------------------------------------------------------------------
1 | table only baz
2 |
--------------------------------------------------------------------------------
/testdata/table_only_paren.golden.sql:
--------------------------------------------------------------------------------
1 | table only baz
2 |
--------------------------------------------------------------------------------
/testdata/table_only_paren.input.sql:
--------------------------------------------------------------------------------
1 | table only (baz)
2 |
--------------------------------------------------------------------------------
/testdata/table_qualified.golden.sql:
--------------------------------------------------------------------------------
1 | table foo.baz
2 |
--------------------------------------------------------------------------------
/testdata/table_qualified.input.sql:
--------------------------------------------------------------------------------
1 | table foo.baz
2 |
--------------------------------------------------------------------------------
/testdata/table_star.golden.sql:
--------------------------------------------------------------------------------
1 | table baz *
2 |
--------------------------------------------------------------------------------
/testdata/table_star.input.sql:
--------------------------------------------------------------------------------
1 | table baz *
2 |
--------------------------------------------------------------------------------
/testdata/treat.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | treat(42 as float8)
3 |
--------------------------------------------------------------------------------
/testdata/treat.input.sql:
--------------------------------------------------------------------------------
1 | select treat(42 as float8)
2 |
--------------------------------------------------------------------------------
/testdata/trim.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | trim(both 'x' from 'xBobxx'),
3 | trim(leading 'x' from 'xBobxx'),
4 | trim(trailing 'x' from 'xBobxx'),
5 | trim(both from 'xBobxx', 'x'),
6 | trim(leading from 'xBobxx', 'x'),
7 | trim(trailing from 'xBobxx', 'x'),
8 | trim(from 'xBobxx', 'x'),
9 | trim(from 'xBobxx'),
10 | trim('xBobxx', 'x'),
11 | trim('xBobxx')
12 |
--------------------------------------------------------------------------------
/testdata/trim.input.sql:
--------------------------------------------------------------------------------
1 | select trim(both 'x' from 'xBobxx'), trim(leading 'x' from 'xBobxx'), trim(trailing 'x' from 'xBobxx'),
2 | trim(both from 'xBobxx', 'x'), trim(leading from 'xBobxx', 'x'), trim(trailing from 'xBobxx', 'x'),
3 | trim(from 'xBobxx', 'x'), trim(from 'xBobxx'),
4 | trim('xBobxx', 'x'), trim('xBobxx')
5 |
--------------------------------------------------------------------------------
/testdata/typecast.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | '42'::integer,
3 | foo::text,
4 | (foo + bar)::text,
5 | '3.14'::numeric(8, 2),
6 | '123.1'::decimal(8, 1),
7 | '424.234'::dec(8, 3),
8 | '324.5'::float(20),
9 | '23.23'::double precision,
10 | 'asdf'::customtype(3),
11 | 'asdf'::myschema.customtype,
12 | '1942'::setof int,
13 | '{123,34}'::int array[4],
14 | '{123,34}'::setof int array[4],
15 | '{123,34}'::int array,
16 | '{123,34}'::setof int array,
17 | 'f'::char,
18 | 'fads'::varchar,
19 | 'fads'::char(10),
20 | 'fads'::varchar(10),
21 | 'f'::char,
22 | 'fads'::varchar,
23 | 'fads'::char(10),
24 | 'fads'::varchar(10),
25 | 'f'::char,
26 | 'fads'::varchar,
27 | 'f'::char,
28 | 'fads'::char(10),
29 | 'asdf'::varchar character set sql_text,
30 | '1'::bit,
31 | '1010'::bit(4),
32 | '1010'::varbit,
33 | '1010'::varbit,
34 | '00:30:00'::interval hour to minute,
35 | '00:15:00'::interval(2)
36 | from
37 | baz
38 |
--------------------------------------------------------------------------------
/testdata/typecast.input.sql:
--------------------------------------------------------------------------------
1 | select '42'::integer, foo::text, (foo+bar)::text, '3.14'::numeric(8,2),
2 | '123.1'::decimal(8,1), '424.234'::dec(8,3),
3 | '324.5'::float(20), '23.23'::double precision,
4 | 'asdf'::customtype(3), 'asdf'::myschema.customtype,
5 | '1942'::setof int,
6 | '{123,34}'::int array[4], '{123,34}'::setof int array[4],
7 | '{123,34}'::int array, '{123,34}'::setof int array,
8 | 'f'::character, 'fads'::character varying,
9 | 'fads'::character(10), 'fads'::character varying(10),
10 | 'f'::char, 'fads'::char varying,
11 | 'fads'::char(10), 'fads'::char varying(10),
12 | 'f'::national character, 'fads'::national character varying,
13 | 'f'::nchar, 'fads'::nchar(10),
14 | 'asdf'::varchar character set sql_text,
15 | '1'::bit, '1010'::bit(4), '1010'::bit varying, '1010'::varbit,
16 | '00:30:00'::interval hour to minute, '00:15:00'::interval(2)
17 |
18 | from baz
19 |
--------------------------------------------------------------------------------
/testdata/unary.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | +11,
3 | -42
4 |
--------------------------------------------------------------------------------
/testdata/unary.input.sql:
--------------------------------------------------------------------------------
1 | select +11, -42
2 |
--------------------------------------------------------------------------------
/testdata/union.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | bar
4 | from
5 | baz
6 | union all
7 | select
8 | a,
9 | b
10 | from
11 | quz
12 |
--------------------------------------------------------------------------------
/testdata/union.input.sql:
--------------------------------------------------------------------------------
1 | select foo, bar from baz union all select a, b from quz
2 |
--------------------------------------------------------------------------------
/testdata/values.golden.sql:
--------------------------------------------------------------------------------
1 | values
2 | (1, 2, 3),
3 | (4, 5, 6),
4 | (7, 8, 9)
5 |
--------------------------------------------------------------------------------
/testdata/values.input.sql:
--------------------------------------------------------------------------------
1 | values(1,2,3), (4,5,6), (7,8,9)
2 |
--------------------------------------------------------------------------------
/testdata/values_default.golden.sql:
--------------------------------------------------------------------------------
1 | values
2 | (1, default, 3),
3 | (4, 5, default),
4 | (default, 8, 9)
5 |
--------------------------------------------------------------------------------
/testdata/values_default.input.sql:
--------------------------------------------------------------------------------
1 | values(1,default,3), (4,5, DEFAULT), (default,8,9)
2 |
--------------------------------------------------------------------------------
/testdata/values_order.golden.sql:
--------------------------------------------------------------------------------
1 | values
2 | (1, 2, 3),
3 | (4, 5, 6),
4 | (7, 8, 9)
5 | order by
6 | 3
7 |
--------------------------------------------------------------------------------
/testdata/values_order.input.sql:
--------------------------------------------------------------------------------
1 | values(1,2,3), (4,5,6), (7,8,9) order by 3
2 |
--------------------------------------------------------------------------------
/testdata/window_function.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over ()
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/window_function.input.sql:
--------------------------------------------------------------------------------
1 | select foo, row_number() over () from baz
2 |
--------------------------------------------------------------------------------
/testdata/window_function_frame.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over (range unbounded preceding),
4 | row_number() over (rows unbounded preceding),
5 | row_number() over (range between unbounded preceding and 3 following),
6 | row_number() over (rows between unbounded preceding and 3 following),
7 | row_number() over (range current row),
8 | row_number() over (rows current row),
9 | row_number() over (range between 2 preceding and unbounded following),
10 | row_number() over (rows between 2 preceding and unbounded following)
11 | from
12 | baz
13 |
--------------------------------------------------------------------------------
/testdata/window_function_frame.input.sql:
--------------------------------------------------------------------------------
1 | select foo,
2 | row_number() over (range unbounded preceding),
3 | row_number() over (rows unbounded preceding),
4 | row_number() over (range between unbounded preceding and 3 following),
5 | row_number() over (rows between unbounded preceding and 3 following),
6 | row_number() over (range current row),
7 | row_number() over (rows current row),
8 | row_number() over (range between 2 preceding and unbounded following),
9 | row_number() over (rows between 2 preceding and unbounded following)
10 | from baz
11 |
--------------------------------------------------------------------------------
/testdata/window_function_named.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over w
4 | from
5 | baz
6 | window
7 | w as (partition by quz order by abc)
8 |
--------------------------------------------------------------------------------
/testdata/window_function_named.input.sql:
--------------------------------------------------------------------------------
1 | select foo, row_number() over w from baz window w as (partition by quz order by abc)
2 |
--------------------------------------------------------------------------------
/testdata/window_function_named_multiple.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over w
4 | from
5 | baz
6 | window
7 | w as (partition by quz),
8 | w2 as (w order by abc)
9 |
--------------------------------------------------------------------------------
/testdata/window_function_named_multiple.input.sql:
--------------------------------------------------------------------------------
1 | select foo, row_number() over w from baz window w as (partition by quz), w2 as (w order by abc)
2 |
--------------------------------------------------------------------------------
/testdata/window_function_order_by.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over (order by quz)
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/window_function_order_by.input.sql:
--------------------------------------------------------------------------------
1 | select foo, row_number() over (order by quz) from baz
2 |
--------------------------------------------------------------------------------
/testdata/window_function_partition_by.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | foo,
3 | row_number() over (partition by quz)
4 | from
5 | baz
6 |
--------------------------------------------------------------------------------
/testdata/window_function_partition_by.input.sql:
--------------------------------------------------------------------------------
1 | select foo, row_number() over (partition by quz) from baz
2 |
--------------------------------------------------------------------------------
/testdata/xmlelement.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlelement(name foo),
3 | xmlelement(name foo, xmlattributes('bar' as baz)),
4 | xmlelement(name foo, xmlattributes(bar, baz)),
5 | xmlelement(name foo, xmlattributes('bar' as baz), 'bo', 'dy'),
6 | xmlelement(name foo, 'bo', 'dy')
7 |
--------------------------------------------------------------------------------
/testdata/xmlelement.input.sql:
--------------------------------------------------------------------------------
1 | select xmlelement(name foo), xmlelement(name foo, xmlattributes('bar' as baz)),
2 | xmlelement(name foo, xmlattributes(bar, baz)),
3 | xmlelement(name foo, xmlattributes('bar' as baz), 'bo', 'dy'),
4 | xmlelement(name foo, 'bo', 'dy')
5 |
--------------------------------------------------------------------------------
/testdata/xmlexists.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlexists('//town[text() = ''Toronto'']' passing 'TorontoOttawa'),
3 | xmlexists('//town[text() = ''Toronto'']' passing by ref 'TorontoOttawa' by ref)
4 |
--------------------------------------------------------------------------------
/testdata/xmlexists.input.sql:
--------------------------------------------------------------------------------
1 | select xmlexists('//town[text() = ''Toronto'']' passing 'TorontoOttawa'),
2 | xmlexists('//town[text() = ''Toronto'']' passing by ref 'TorontoOttawa' by ref)
3 |
--------------------------------------------------------------------------------
/testdata/xmlforest.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlforest('abc' as foo, 'xyz' as bar, baz)
3 |
--------------------------------------------------------------------------------
/testdata/xmlforest.input.sql:
--------------------------------------------------------------------------------
1 | select xmlforest('abc' as foo, 'xyz' as bar, baz)
2 |
--------------------------------------------------------------------------------
/testdata/xmlparse.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlparse(document 'John'),
3 | xmlparse(content 'John'),
4 | xmlparse(content 'John' preserve whitespace),
5 | xmlparse(content 'John' strip whitespace)
6 |
--------------------------------------------------------------------------------
/testdata/xmlparse.input.sql:
--------------------------------------------------------------------------------
1 | select xmlparse(document 'John'),
2 | xmlparse(content 'John'),
3 | xmlparse(content 'John' preserve whitespace),
4 | xmlparse(content 'John' strip whitespace)
5 |
--------------------------------------------------------------------------------
/testdata/xmlpi.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlpi(name foo),
3 | xmlpi(name foo, 'bar')
4 |
--------------------------------------------------------------------------------
/testdata/xmlpi.input.sql:
--------------------------------------------------------------------------------
1 | select xmlpi(name foo), xmlpi(name foo, 'bar')
2 |
--------------------------------------------------------------------------------
/testdata/xmlroot.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlroot(xmlparse(document 'abc'), version '1.0', standalone yes),
3 | xmlroot(xmlparse(document 'abc'), version '1.0', standalone no),
4 | xmlroot(xmlparse(document 'abc'), version '1.0', standalone no value),
5 | xmlroot(xmlparse(document 'abc'), version '1.0'),
6 | xmlroot(xmlparse(document 'abc'), version no value)
7 |
--------------------------------------------------------------------------------
/testdata/xmlroot.input.sql:
--------------------------------------------------------------------------------
1 | select xmlroot(xmlparse(document 'abc'), version '1.0', standalone yes),
2 | xmlroot(xmlparse(document 'abc'), version '1.0', standalone no),
3 | xmlroot(xmlparse(document 'abc'), version '1.0', standalone no value),
4 | xmlroot(xmlparse(document 'abc'), version '1.0'),
5 | xmlroot(xmlparse(document 'abc'), version no value)
6 |
--------------------------------------------------------------------------------
/testdata/xmlserialize.golden.sql:
--------------------------------------------------------------------------------
1 | select
2 | xmlserialize(content 'bar' as text),
3 | xmlserialize(document 'bar' as text)
4 |
--------------------------------------------------------------------------------
/testdata/xmlserialize.input.sql:
--------------------------------------------------------------------------------
1 | select xmlserialize(content 'bar' as text), xmlserialize(document 'bar' as text)
2 |
--------------------------------------------------------------------------------
/token_renderer.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | import (
4 | "bytes"
5 | )
6 |
7 | type TokenRenderer []RenderToken
8 |
9 | func (r *TokenRenderer) Text(val string, tokenType int) {
10 | *r = TokenRenderer(append([]RenderToken(*r), RenderToken{Type: tokenType, Value: val}))
11 | }
12 |
13 | func (r *TokenRenderer) Control(tokenType int) {
14 | *r = TokenRenderer(append([]RenderToken(*r), RenderToken{Type: tokenType}))
15 | }
16 |
17 | func RenderTokens(r Renderer, tokens []RenderToken) {
18 | for _, t := range tokens {
19 | if t.Value != "" {
20 | r.Text(t.Value, t.Type)
21 | } else {
22 | r.Control(t.Type)
23 | }
24 | }
25 | }
26 |
27 | func TryOneLine(tokens []RenderToken, maxLineLength int) []RenderToken {
28 | buf := &bytes.Buffer{}
29 | r := NewTextRenderer(buf)
30 | RenderTokens(r, tokens)
31 | if buf.Len() < maxLineLength {
32 | filteredTokens := []RenderToken{}
33 | for _, t := range tokens {
34 | switch t.Type {
35 | case NewLineToken, IndentToken, UnindentToken:
36 | // filter these out
37 | default:
38 | filteredTokens = append(filteredTokens, t)
39 | }
40 | }
41 |
42 | tokens = filteredTokens
43 | }
44 |
45 | return tokens
46 | }
47 |
--------------------------------------------------------------------------------
/token_renderer_test.go:
--------------------------------------------------------------------------------
1 | package sqlfmt
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestTokenRenderer(t *testing.T) {
9 | r := TokenRenderer(nil)
10 | r.Text("select", KeywordToken)
11 | r.Control(NewLineToken)
12 | r.Control(IndentToken)
13 | r.Text("foo", IdentifierToken)
14 | r.Control(NewLineToken)
15 | r.Control(UnindentToken)
16 | r.Text("from", KeywordToken)
17 | r.Control(NewLineToken)
18 | r.Control(IndentToken)
19 | r.Text("bar", IdentifierToken)
20 |
21 | expected := []RenderToken{
22 | {Type: KeywordToken, Value: "select"},
23 | {Type: NewLineToken},
24 | {Type: IndentToken},
25 | {Type: IdentifierToken, Value: "foo"},
26 | {Type: NewLineToken},
27 | {Type: UnindentToken},
28 | {Type: KeywordToken, Value: "from"},
29 | {Type: NewLineToken},
30 | {Type: IndentToken},
31 | {Type: IdentifierToken, Value: "bar"},
32 | }
33 |
34 | if !reflect.DeepEqual([]RenderToken(r), expected) {
35 | t.Errorf("Expected `%v`, got `%v`", expected, []RenderToken(r))
36 | }
37 | }
38 |
--------------------------------------------------------------------------------