Experimental SQL formatter with width-aware output
Based on http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf.
SQL formatter with width-aware output
Home Page: https://sqlfum.pt
License: Apache License 2.0
Experimental SQL formatter with width-aware output
Based on http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf.
I've definitely ran make
in the $GOPATH/src/github.com/cockroachdb/cockroach
dir, but still seem to be getting this undefined errors.
โ sqlfmt git:(master) bash build.sh
+ IMG=gcr.io/hots-cockroach/sqlfmt:latest
+ go build -o sqlfmt
# github.com/cockroachdb/cockroach/pkg/sql/lex
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:10:32: undefined: ABORT
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:11:32: undefined: ACTION
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:12:32: undefined: ADD
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:13:32: undefined: ADMIN
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:14:32: undefined: AGGREGATE
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:15:32: undefined: ALL
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:16:32: undefined: ALTER
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:17:32: undefined: ANALYSE
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:18:32: undefined: ANALYZE
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:19:32: undefined: AND
../../cockroachdb/cockroach/pkg/sql/lex/keywords.go:19:32: too many errors
see example below:
SELECT len(ISNUll(tableName.clumn,'')) AS CloumnLength FROM tableName;
The formatted SQL was:
SELECT len("isnull"(tablename.clumn, '')) AS cloumnlength FROM tablename;
Additional double quotation marks were added around isnull
, it was incorrect.
This library uses the CockroachDB SQL parser, which effectively limits it to use with just CockroachDB, since CockroachDB lacks significant amounts of SQL support: WITH RECURSIVE
, CREATE FUNCTION
, and CREATE TYPE
. And that's just standard ANSI SQL.
IMO, this should either
advertise as CockroachDB SQL formatter
fork from the CockroachDB parser so as to allow contributions to support SQL syntax (regardless of CockroachDB support).
It would be great if we can add support for gopherjs so that we can use the formatter in the browser.
C.f.,
SELECT DISTINCT
category0_.id AS id1_0_0_,
segmentati1_.category_id AS category1_9_1_,
segmentati1_.segmentation_id AS segmenta2_9_1_,
segmentati2_.id AS id1_8_2_,
inputtype3_.id AS id1_4_3_,
operator5_.id AS id1_5_4_,
configurat6_.key AS key1_1_5_,
configurat6_.segmentation_id AS segmenta2_1_5_,
options7_.key AS key1_6_6_,
...
FROM category AS category0_
INNER JOIN segmentation_category AS segmentati1_ ON category0_.id = segmentati1_.category_id
INNER JOIN segmentation AS segmentati2_ ON segmentati1_.segmentation_id = segmentati2_.id
INNER JOIN input_type AS inputtype3_ ON segmentati2_.input_type_id = inputtype3_.id
LEFT JOIN segmentation_operator AS operators4_ ON segmentati2_.id = operators4_.segmentation_id
LEFT JOIN operator AS operator5_ ON operators4_.operator_id = operator5_.id
LEFT JOIN configuration AS configurat6_ ON segmentati2_.id = configurat6_.segmentation_id
LEFT JOIN option AS options7_ ON segmentati2_.id = options7_.segmentation_id
LEFT JOIN context AS context8_ ON segmentati2_.context_id = context8_.id
LEFT JOIN permission AS permission9_ ON segmentati2_.id = permission9_.segmentation_id AND permission9_.mnemonic = '?'
LEFT JOIN feature AS feature10_ ON segmentati2_.feature_id = feature10_.id
WHERE category0_.active = true
AND segmentati2_.active = true
AND (permission9_.mnemonic IS NULL AND permission9_.segmentation_id IS NULL AND segmentati2_.limited = false OR permission9_.authorized = true)
AND (feature10_.id IS NULL OR feature10_.active = true AND feature10_.key IN ('?', '?', '?', '?', '?', '?', '?', '?'))
ORDER BY category0_.ordinality, options7_.display;
vs.
SELECT DISTINCT
category0_.id AS id1_0_0_,
segmentati1_.category_id AS category1_9_1_,
segmentati1_.segmentation_id AS segmenta2_9_1_,
segmentati2_.id AS id1_8_2_,
inputtype3_.id AS id1_4_3_,
operator5_.id AS id1_5_4_,
configurat6_.key AS key1_1_5_,
configurat6_.segmentation_id AS segmenta2_1_5_,
options7_.key AS key1_6_6_,
options7_.segmentation_id AS segmenta2_6_6_,
context8_.id AS id1_2_7_,
category0_.created_on AS created_2_0_0_,
category0_.updated_on AS updated_3_0_0_,
category0_.active AS active4_0_0_,
category0_.description AS descript5_0_0_,
category0_.display AS display6_0_0_,
category0_.key AS key7_0_0_,
category0_.ordinality AS ordinali8_0_0_,
segmentati1_.created_on AS created_3_9_1_,
segmentati1_.updated_on AS updated_4_9_1_,
segmentati1_.ordinality AS ordinali5_9_1_,
segmentati1_.category_id AS category1_9_0__,
segmentati1_.segmentation_id AS segmenta2_9_0__,
segmentati1_.ordinality AS ordinali5_0__,
segmentati2_.created_on AS created_2_8_2_,
segmentati2_.updated_on AS updated_3_8_2_,
segmentati2_.active AS active4_8_2_,
segmentati2_.attribute AS attribut5_8_2_,
segmentati2_.context_id AS context12_8_2_,
segmentati2_.description AS descript6_8_2_,
segmentati2_.display AS display7_8_2_,
segmentati2_.feature_id AS feature13_8_2_,
segmentati2_.input_type_id AS input_t14_8_2_,
segmentati2_.key AS key8_8_2_,
segmentati2_.limited AS limited9_8_2_,
segmentati2_.options_src AS options10_8_2_,
segmentati2_.title AS title11_8_2_,
inputtype3_.created_on AS created_2_4_3_,
inputtype3_.updated_on AS updated_3_4_3_,
inputtype3_.active AS active4_4_3_,
inputtype3_.json_value AS json_val5_4_3_,
inputtype3_.name AS name6_4_3_,
operator5_.created_on AS created_2_5_4_,
operator5_.updated_on AS updated_3_5_4_,
operator5_.display AS display4_5_4_,
operator5_.key AS key5_5_4_,
operators4_.segmentation_id AS segmenta1_10_1__,
operators4_.operator_id AS operator2_10_1__,
configurat6_.created_on AS created_3_1_5_,
configurat6_.updated_on AS updated_4_1_5_,
configurat6_.bool_value AS bool_val5_1_5_,
configurat6_.string_value AS string_v6_1_5_,
configurat6_.segmentation_id AS segmenta2_1_2__,
configurat6_.key AS key1_1_2__,
options7_.created_on AS created_3_6_6_,
options7_.updated_on AS updated_4_6_6_,
options7_.display AS display5_6_6_,
options7_.segmentation_id AS segmenta2_6_3__,
options7_.key AS key1_6_3__,
context8_.created_on AS created_2_2_7_,
context8_.updated_on AS updated_3_2_7_,
context8_.key AS key4_2_7_
FROM category AS category0_
INNER JOIN segmentation_category AS segmentati1_ ON category0_.id = segmentati1_.category_id
INNER JOIN segmentation AS segmentati2_ ON segmentati1_.segmentation_id = segmentati2_.id
INNER JOIN input_type AS inputtype3_ ON segmentati2_.input_type_id = inputtype3_.id
LEFT JOIN segmentation_operator AS operators4_ ON segmentati2_.id = operators4_.segmentation_id
LEFT JOIN operator AS operator5_ ON operators4_.operator_id = operator5_.id
LEFT JOIN configuration AS configurat6_ ON segmentati2_.id = configurat6_.segmentation_id
LEFT JOIN option AS options7_ ON segmentati2_.id = options7_.segmentation_id
LEFT JOIN context AS context8_ ON segmentati2_.context_id = context8_.id
LEFT JOIN permission AS permission9_ ON segmentati2_.id = permission9_.segmentation_id AND permission9_.mnemonic = '?'
LEFT JOIN feature AS feature10_ ON segmentati2_.feature_id = feature10_.id
WHERE category0_.active = true
AND segmentati2_.active = true
AND (permission9_.mnemonic IS NULL AND permission9_.segmentation_id IS NULL AND segmentati2_.limited = false OR permission9_.authorized = true)
AND (feature10_.id IS NULL OR feature10_.active = true AND feature10_.key IN ('?', '?', '?', '?', '?', '?', '?', '?'))
ORDER BY category0_.ordinality, options7_.display;
select row(1, 2)
is removed even with simplify off
Hi there. This formats beautifully, but I have trouble using it with PostGIS (e.g. it doesn't like the distance operator, <->
). Is there any way it could be made a bit more flexible? Perhaps it could take some kind of supplementary list of operators/keywords etc, and possibly some hints about how to deal with them??
select a,b,c from x union all select d,e,f from y
SELECT d1 AT TIME ZONE 'Europe/Paris' FROM t1
produces error message :
unimplemented at or near "from"
date AT TIME ZONE timezone
is a somehow classic construct.
References :
Maybe have a function that wraps node types with annotations and a configuration file that maps node types to colors and another that shows how to output color (i.e., html, ascii).
Let me know if this is the wrong place to file this. I didn't see any mention of comments in the README or in the sqlfmt blog post.
Given a section of a SQL file with comments like this:
-- How many users took rides that ended in NY?
SELECT COUNT(*) FROM (SELECT users.name, users.address, rides.end_address FROM users JOIN rides ON users.id = rides.rider_id WHERE users.address LIKE '%NY%');
-- +-------+
-- | count |
-- +-------+
-- | 2802 |
-- +-------+
-- (1 row)
If I run that selection through cockroach sqlfmt --use-spaces --align --print-width=78 --no-simplify
in my editor, it strips out my comments and results in:
SELECT count(*)
FROM (
SELECT users.name, users.address, rides.end_address
FROM users JOIN rides ON users.id = rides.rider_id
WHERE users.address LIKE '%NY%'
)
I would like to set up my editor to autoformat SQL code on save, but I use comments for notes to myself, etc. Possible to leave comments alone as they pass through? Happy for an option (which I understand you may prefer less) or for ignoring comments to be the default.
From the docs: https://www.postgresql.org/docs/9.1/queries-with.html
Trying to fumpt this:
WITH RECURSIVE included_parts(sub_part, part, quantity) AS (
SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'
UNION ALL
SELECT p.sub_part, p.part, p.quantity
FROM included_parts pr, parts p
WHERE p.part = pr.sub_part
)
SELECT sub_part, SUM(quantity) as total_quantity
FROM included_parts
GROUP BY sub_part
results in unimplemented at or near "select"
Removing RECURSIVE
will let it format successfully.
Permissive license would be super-nice!
Hi, I was hoping to use the --no-extra-line
flag but it is not released yet.
Would it be possible to cut a new release? Thanks!
The common ones that I can think of are:
?
$0
, $1
...%0
, %1
...I guess, it's probably best to handle ?
, $
, %
with and without numbers.
e.g.
SELECT * FROM pg_catalog.pg_foreign_table WHERE ftrelid = ?;
https://github.com/jondot/goweight says:
9.9 MB github.com/cockroachdb/cockroach/pkg/roachpb
7.0 MB github.com/cockroachdb/cockroach/vendor/github.com/DataDog/zstd
6.2 MB github.com/cockroachdb/cockroach/pkg/sql/sem/tree
4.5 MB github.com/cockroachdb/cockroach/pkg/sql/sem/builtins
4.2 MB github.com/cockroachdb/cockroach/pkg/sql/sqlbase
3.4 MB github.com/cockroachdb/cockroach/pkg/sql/parser
3.4 MB net/http
3.2 MB github.com/cockroachdb/cockroach/vendor/github.com/Shopify/sarama
3.0 MB runtime
2.5 MB github.com/cockroachdb/cockroach/vendor/github.com/gogo/protobuf/proto
1.8 MB github.com/cockroachdb/cockroach/vendor/github.com/gogo/protobuf/types
1.7 MB github.com/cockroachdb/cockroach/vendor/golang.org/x/net/http2
1.7 MB github.com/cockroachdb/cockroach/vendor/github.com/lightstep/lightstep-tracer-go/thrift_0_9_2/lib/go/thrift
1.7 MB github.com/cockroachdb/cockroach/vendor/github.com/apache/thrift/lib/go/thrift
1.7 MB net
1.6 MB github.com/cockroachdb/cockroach/vendor/github.com/golang/protobuf/proto
1.4 MB github.com/cockroachdb/cockroach/vendor/golang.org/x/text/collate
1.4 MB github.com/cockroachdb/cockroach/vendor/github.com/aws/aws-sdk-go/aws/endpoints
1.4 MB reflect
1.2 MB github.com/cockroachdb/cockroach/vendor/golang.org/x/sys/unix
1.2 MB github.com/cockroachdb/cockroach/vendor/google.golang.org/grpc
1.1 MB github.com/cockroachdb/cockroach/pkg/internal/client
1.0 MB github.com/spf13/pflag
1.0 MB github.com/cockroachdb/cockroach/vendor/github.com/prometheus/client_golang/prometheus
991 kB github.com/cockroachdb/cockroach/pkg/util/json
975 kB github.com/cockroachdb/cockroach/vendor/gopkg.in/yaml.v2
...
When you click a share link auto-paste should never fire.
this will allow huge queries to not blindly fail.
also show errors when the request returns an error.
SELECT
this_.studentid AS studenti1_26_0_,
this_.name AS name2_26_0_,
this_.address_city AS address_3_26_0_,
this_.address_state AS address_4_26_0_,
this_.preferredcoursecode AS preferre5_26_0_
FROM
student AS this_
WHERE
EXISTS(
SELECT
enrolment_.studentid AS y0_
FROM
enrolment AS enrolment_
WHERE
enrolment_.year
= (
SELECT
max(maxstudentenrolment_.year) AS y0_
FROM
enrolment AS maxstudentenrolment_
WHERE
this_.preferredcoursecode = maxstudentenrolment_.coursecode
)
);
SELECT
this_.person_id AS person_i1_2_0_,
this_.version AS version2_2_0_,
this_.name AS name3_2_0_,
this_.sex AS sex4_2_0_,
this_.height_centimeters / 2.54E0 AS height_c5_2_0_,
this_.address AS address6_2_0_,
this_.zip AS zip7_2_0_,
this_.country AS country8_2_0_,
this_1_.title AS title2_0_0_,
this_1_.salary AS salary3_0_0_,
this_1_.pwd_expiry_weeks * 7.0E0 AS pwd_expi4_0_0_,
this_1_.manager AS manager5_0_0_,
this_2_.comments AS comments2_1_0_,
this_2_.salesperson AS salesper3_1_0_
FROM A as this_;
vs
SELECT this_.person_id AS person_i1_2_0_
,this_.version AS version2_2_0_,
,this_.name AS name3_2_0_,
,this_.sex AS sex4_2_0_
,this_.height_centimeters / 2.54E0 AS height_c5_2_0_
,this_.address AS address6_2_0_
,this_.zip AS zip7_2_0_
,this_.country AS country8_2_0_
,this_1_.title AS title2_0_0_
,this_1_.salary AS salary3_0_0_
,this_1_.pwd_expiry_weeks * 7.0E0 AS pwd_expi4_0_0_
,this_1_.manager AS manager5_0_0_
,this_2_.comments AS comments2_1_0_
,this_2_.salesperson AS salesper3_1_0_
FROM A as this_;
ref:
https://blog.2ndquadrant.com/postgresql-10-identity-columns/
https://www.postgresql.org/docs/10/sql-createtable.html
CREATE TABLE test_new (
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
payload text
);
Attempt to fumpt...
syntax error at or near "generated"
Some implementations of SQL are case sensitive, this can be destructive.
https://stackoverflow.com/questions/153944/is-sql-syntax-case-sensitive
This query is broken, badly:
Here's my hand formatted version:
SELECT array_to_string(array_agg(sql), e'\\n\\n') AS sql
FROM (
SELECT (
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
CASE WHEN rolcanlogin
THEN '-- User: '
ELSE '-- Role: '
END || quote_ident(rolname)
) || e'\\n-- DROP '
) || CASE WHEN rolcanlogin
THEN 'USER '
ELSE 'ROLE '
END
) || quote_ident(rolname)
) || e';\\n\\nCREATE '
) || CASE WHEN rolcanlogin
THEN 'USER '
ELSE 'ROLE '
END
) || quote_ident(rolname)
) || e' WITH\\n '
) || CASE WHEN rolcanlogin
THEN 'LOGIN'
ELSE 'NOLOGIN'
END
) || e'\\n '
) || CASE WHEN rolcanlogin AND (rolpassword LIKE 'md5%')
THEN ('ENCRYPTED PASSWORD ' || quote_literal(rolpassword)) || e'\\n '
ELSE ''
END
) || CASE WHEN rolsuper
THEN 'SUPERUSER'
ELSE 'NOSUPERUSER'
END
) || e'\\n '
) || CASE WHEN rolinherit
THEN 'INHERIT'
ELSE 'NOINHERIT'
END
) || e'\\n '
) || CASE WHEN rolcreatedb
THEN 'CREATEDB'
ELSE 'NOCREATEDB'
END
) || e'\\n '
) || CASE WHEN rolcreaterole
THEN 'CREATEROLE'
ELSE 'NOCREATEROLE'
END
) || e'\\n '
) || CASE WHEN rolreplication
THEN 'REPLICATION'
ELSE 'NOREPLICATION'
END
) || CASE WHEN rolconnlimit > 0
THEN e'\\n CONNECTION LIMIT ' || rolconnlimit
ELSE ''
END
) || CASE WHEN rolvaliduntil IS NOT NULL
THEN e'\\n VALID UNTIL ' || quote_literal(rolvaliduntil::TEXT)
ELSE ';'
END AS sql
FROM pg_roles AS r
WHERE r.oid = 823966177::OID
UNION ALL
(
SELECT array_to_string(array_agg(sql), e'\\n') AS sql
FROM (
SELECT (
(
(
'GRANT ' || array_to_string(array_agg(rolname), ', ')
) || ' TO '
) || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))
) || CASE WHEN admin_option
THEN ' WITH ADMIN OPTION;'
ELSE ';'
END AS sql
FROM (
SELECT quote_ident(r.rolname) AS rolname,
m.admin_option AS admin_option
FROM pg_auth_members AS m
LEFT JOIN pg_roles AS r
ON (m.roleid = r.oid)
WHERE m.member = 823966177::OID
ORDER BY r.rolname
) AS a
GROUP BY admin_option
) AS s
)
UNION ALL
(
SELECT array_to_string(array_agg(sql), e'\\n') AS sql
FROM (
SELECT (
(
(
(
(
(
'ALTER ' || CASE WHEN rolcanlogin
THEN 'USER '
ELSE 'ROLE '
END
) || pg_catalog.quote_ident(rolname)
) || ' SET '
) || param
) || ' TO '
) || CASE WHEN param IN ('search_path', 'temp_tablespaces')
THEN value
ELSE quote_literal(value)
END
) || ';' AS sql
FROM (
SELECT rolcanlogin,
rolname,
split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
FROM (
SELECT unnest(rolconfig) AS rolconfig,
rolcanlogin,
rolname
FROM pg_catalog.pg_roles
WHERE oid = 823966177::OID) AS r
) AS a
) AS b
)
UNION ALL
(
SELECT array_to_string(array_agg(sql), e'\\n') AS sql
FROM (
SELECT (
(
(
(
(
(
(
'ALTER ROLE ' || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))
) || ' IN DATABASE '
) || pg_catalog.quote_ident(datname)
) || ' SET '
) || param
) || ' TO '
) || CASE WHEN param IN ('search_path', 'temp_tablespaces')
THEN value
ELSE quote_literal(value)
END
) || ';' AS sql
FROM (
SELECT datname,
split_part(rolconfig, '=', 1) AS param,
replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value
FROM (
SELECT d.datname,
unnest(c.setconfig) AS rolconfig
FROM (
SELECT *
FROM pg_catalog.pg_db_role_setting AS dr
WHERE (dr.setrole = 823966177::OID)
AND (dr.setdatabase != 0)
) AS c
LEFT JOIN pg_catalog.pg_database AS d
ON (d.oid = c.setdatabase)
) AS a
) AS b
) AS d
)
UNION ALL
(
SELECT (
(
(
'COMMENT ON ROLE ' || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))
) || ' IS '
) || pg_catalog.quote_literal(description)
) || ';' AS sql
FROM (
SELECT pg_catalog.shobj_description(823966177::OID, 'pg_authid') AS description
) AS a
WHERE description IS NOT NULL
)
UNION ALL
(
SELECT array_to_string(array_agg(sql), e'\\n') AS sql
FROM (
SELECT (
(
(
(
(
'SECURITY LABEL FOR ' || provider
) || e'\\n ON ROLE '
) || pg_catalog.quote_ident(rolname)
) || e'\\n IS '
) || pg_catalog.quote_literal(label)
) || ';' AS sql
FROM (
SELECT label,
provider,
rolname
FROM (
SELECT *
FROM pg_shseclabel AS sl1
WHERE sl1.objoid = 823966177::OID
) AS s
LEFT JOIN pg_catalog.pg_roles AS r
ON (s.objoid = r.oid)
) AS a
) AS b
)
) AS a
Here's the current output:
SELECT array_to_string(array_agg(sql), e'\\n\\n') AS sql
FROM
(
SELECT (((((((((((((((((((((CASE WHEN rolcanlogin THEN '-- User: ' ELSE '-- Role: ' END || quote_ident(rolname)) || e'\\n-- DROP ') || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END) || quote_ident(rolname)) || e';\\n\\nCREATE ') || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END) || quote_ident(rolname)) || e' WITH\\n ') || CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END) || e'\\n ') || CASE WHEN rolcanlogin AND (rolpassword LIKE 'md5%') THEN ('ENCRYPTED PASSWORD ' || quote_literal(rolpassword)) || e'\\n ' ELSE '' END) || CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END) || e'\\n ') || CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END) || e'\\n ') || CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END) || e'\\n ') || CASE WHEN rolcreaterole THEN 'CREATEROLE' ELSE 'NOCREATEROLE' END) || e'\\n ') || CASE WHEN rolreplication THEN 'REPLICATION' ELSE 'NOREPLICATION' END) || CASE WHEN rolconnlimit > 0 THEN e'\\n CONNECTION LIMIT ' || rolconnlimit ELSE '' END) || CASE WHEN rolvaliduntil IS NOT NULL THEN e'\\n VALID UNTIL ' || quote_literal(rolvaliduntil::TEXT) ELSE ';' END AS sql FROM pg_roles AS r WHERE r.oid = 823966177::OID UNION ALL (SELECT array_to_string(array_agg(sql), e'\\n') AS sql FROM (SELECT ((('GRANT ' || array_to_string(array_agg(rolname), ', ')) || ' TO ') || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))) || CASE WHEN admin_option THEN ' WITH ADMIN OPTION;' ELSE ';' END AS sql FROM (SELECT quote_ident(r.rolname) AS rolname, m.admin_option AS admin_option FROM pg_auth_members AS m LEFT JOIN pg_roles AS r ON (m.roleid = r.oid) WHERE m.member = 823966177::OID ORDER BY r.rolname) AS a GROUP BY admin_option) AS s) UNION ALL (SELECT array_to_string(array_agg(sql), e'\\n') AS sql FROM (SELECT (((((('ALTER ' || CASE WHEN rolcanlogin THEN 'USER ' ELSE 'ROLE ' END) || pg_catalog.quote_ident(rolname)) || ' SET ') || param) || ' TO ') || CASE WHEN param IN ('search_path', 'temp_tablespaces') THEN value ELSE quote_literal(value) END) || ';' AS sql FROM (SELECT rolcanlogin, rolname, split_part(rolconfig, '=', 1) AS param, replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value FROM (SELECT unnest(rolconfig) AS rolconfig, rolcanlogin, rolname FROM pg_catalog.pg_roles WHERE oid = 823966177::OID) AS r) AS a) AS b) UNION ALL (SELECT array_to_string(array_agg(sql), e'\\n') AS sql FROM (SELECT ((((((('ALTER ROLE ' || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))) || ' IN DATABASE ') || pg_catalog.quote_ident(datname)) || ' SET ') || param) || ' TO ') || CASE WHEN param IN ('search_path', 'temp_tablespaces') THEN value ELSE quote_literal(value) END) || ';' AS sql FROM (SELECT datname, split_part(rolconfig, '=', 1) AS param, replace(rolconfig, split_part(rolconfig, '=', 1) || '=', '') AS value FROM (SELECT d.datname, unnest(c.setconfig) AS rolconfig FROM (SELECT * FROM pg_catalog.pg_db_role_setting AS dr WHERE (dr.setrole = 823966177::OID) AND (dr.setdatabase != 0)) AS c LEFT JOIN pg_catalog.pg_database AS d ON (d.oid = c.setdatabase)) AS a) AS b) AS d) UNION ALL (SELECT ((('COMMENT ON ROLE ' || pg_catalog.quote_ident(pg_get_userbyid(823966177::OID))) || ' IS ') || pg_catalog.quote_literal(description)) || ';' AS sql FROM (SELECT pg_catalog.shobj_description(823966177::OID, 'pg_authid') AS description) AS a WHERE description IS NOT NULL) UNION ALL (SELECT array_to_string(array_agg(sql), e'\\n') AS sql FROM (SELECT ((((('SECURITY LABEL FOR ' || provider) || e'\\n ON ROLE ') || pg_catalog.quote_ident(rolname)) || e'\\n IS ') || pg_catalog.quote_literal(label)) || ';' AS sql FROM (SELECT label, provider, rolname FROM (SELECT * FROM pg_shseclabel AS sl1 WHERE sl1.objoid = 823966177::OID) AS s LEFT JOIN pg_catalog.pg_roles AS r ON (s.objoid = r.oid)) AS a) AS b)
)
AS a;
Taken from cockroachdb/cockroach#26584
CREATE TABLE foo (a INT CONSTRAINT another_name PRIMARY KEY)
cc @knz
It appears that functions are not implemented. I would think function would be pretty standard across dialects though from what I understand this is still a work in progress.
It would be nice to have a list of features that are not yet implemented.
SELECT _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ FROM ((SELECT _, _, _, _, _, _, _, _, _, _ FROM _._._ WHERE (_ = (-_)) AND (_ = _) ORDER BY _, _, _ LIMIT _) JOIN (SELECT _, _, _, _, _, _, _, _, _, _, _, _, _, _ FROM _._._ WHERE (_ = (-_)) AND (_ = _) ORDER BY _, _ LIMIT _) USING (_, _)) ORDER BY _, _, _
SELECT ref0_.generationuser_id AS generati1_2_0_
,ref0_.ref_id AS ref_id2_2_0_
,(SELECT a13.age
FROM generationgroup AS a13
WHERE a13.id = ref0_.ref_id) AS formula131_0_
,(SELECT a15.culture
FROM generationgroup AS a15
WHERE a15.id = ref0_.ref_id) AS formula132_0_
,(SELECT a13.description
FROM generationgroup AS a13
WHERE a13.id = ref0_.ref_id) AS formula133_0_
,generation1_.id AS id1_0_1_
,generation1_.age AS age2_0_1_
,generation1_.culture AS culture3_0_1_
,generation1_.description AS descript4_0_1_
FROM generationuser_generationgroup AS ref0_
INNER JOIN generationgroup AS generation1_
ON ref0_.ref_id = generation1_.id
WHERE ref0_.generationuser_id = 1;
The AS ref0_ inner join is really weirdly formatted. The AS should be indented, the inner join not, but the sub part of inner join should be indented.
Trying to build the sqlfmt
binary for local use.
Have $GOPATH/src/.../cockroach
with master branch checked out (HEAD d404a3f64b)
sqlfmt HEAD is currently 8113004
$ go build -o sqlfmt
# github.com/cockroachdb/cockroach/pkg/sql/parser
yaccpar:624: undefined: tree.TestingRelocate
yaccpar:631: undefined: tree.TestingRelocate
yaccpar:2349: undefined: tree.SnapshotIsolation
sqlfum.pt works great, except a pain to always have to search and replace my string literals to double quotes ". :(
Hi Matt,
Any plan to split out the formatting part out to a module?
Hi,
Do you have any plans on pretty printing JSON values? For example, something like "INSERT INTO my_table (json_col) VALUES ('{ ... a long JSON document ... }');" prints the entire JSON on a single line.
cuz they change the textarea position too
CREATE TABLE t (
created_at TIMESTAMP DEFAULT current_timestamp NOT NULL
);
generates
CREATE TABLE t (
created_at TIMESTAMP
DEFAULT current_timestamp()
NOT NULL
);
Note the ()
after current_timestamp, this is invalid syntax for postgres. I'm guessing you're normalizing to the built in function names as documented on https://www.cockroachlabs.com/docs/stable/functions-and-operators.html#special-syntax-forms
I know you're focused on cockroach syntax, but this formatting nit keeps your otherwise super useful utility from being useful for postgres too.
Have you considered a dialect option, or something else that would allow sqlfmt to work for non-cockroach dialects? I understand the maintenance burden on you that sort of option could entail, but this tool is the best sql formatter I've come across, so I thought I'd ask.
SELECT
this_.person_id AS person_i1_2_0_,
this_.version AS version2_2_0_,
this_.name AS name3_2_0_,
this_.sex AS sex4_2_0_,
this_.height_centimeters / 2.54E0 AS height_c5_2_0_,
this_.address AS address6_2_0_,
this_.zip AS zip7_2_0_,
this_.country AS country8_2_0_,
this_1_.title AS title2_0_0_,
this_1_.salary AS salary3_0_0_,
this_1_.pwd_expiry_weeks * 7.0E0 AS pwd_expi4_0_0_,
this_1_.manager AS manager5_0_0_,
this_2_.comments AS comments2_1_0_,
this_2_.salesperson AS salesper3_1_0_,
CASE WHEN this_1_.person_id IS NOT NULL THEN 1 WHEN this_2_.person_id IS NOT NULL THEN 2 WHEN this_.person_id IS NOT NULL THEN 0 END
AS clazz_0_
FROM
jperson AS this_ LEFT JOIN jemployee AS this_1_ ON this_.person_id = this_1_.person_id
LEFT JOIN jmanager AS this_2_ ON this_.person_id = this_2_.person_id
WHERE (this_.address, this_.zip, this_.country) IN (($1, $2, $3), ($4, $5, $6));
Obviously the case looks bad, but did that cause the first LEFT JOIN to not be on the next line?
I'm a Go noob, so I'm not quite certain where go to from here, and the docs on building are quite sparse at this point. Any clues on getting the following error set when trying to build?
/p/t/sqlfmt> sh build.sh
+ IMG=gcr.io/hots-cockroach/sqlfmt:latest
+ go build -o sqlfmt
# github.com/cockroachdb/cockroach/pkg/sql/lex
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:10:32: undefined: ABORT
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:11:32: undefined: ACTION
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:12:32: undefined: ADD
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:13:32: undefined: ADMIN
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:14:32: undefined: ALL
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:15:32: undefined: ALTER
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:16:32: undefined: ANALYSE
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:17:32: undefined: ANALYZE
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:18:32: undefined: AND
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:19:32: undefined: ANNOTATE_TYPE
/Users/andrew/go/src/github.com/cockroachdb/cockroach/pkg/sql/lex/keywords.go:19:32: too many errors
Been using the website to help format some of my postgres queries. When I happen to use interval in my queries I end up getting an interval that doesn't work in postgres.
I get something like'36:00:00':::INTERVAL
which should be '36:00:00'::INTERVAL
for postgres. Looks like an extra :
is being used when doing the cast.
Sample input:
select date_trunc('hour', time_column), count(*)
from import_table
where time_column > date_trunc('hour', now()) - interval '36 hours'
group by date_trunc('hour', time_column)
order by 1;
Sample output:
SELECT
date_trunc('hour', time_column), count(*)
FROM
import_table
WHERE
time_column > date_trunc('hour', now()) - '36:00:00':::INTERVAL
GROUP BY
date_trunc('hour', time_column)
ORDER BY
1;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.