Skip to content

Commit af1016f

Browse files
kyleconroyclaude
andauthored
clickhouse: type inference via the new analysis core (analyze only) (#4521)
* core: land xqlc catalog + analyzer on sqlc's AST Port xqlc's core catalog (SQLite-backed sql_* catalog) and its dialect-neutral query analyzer into internal/core, repointing the analyzer from xqlc's copy of the AST onto sqlc's internal/sql/ast so there is a single AST. No converter and no second AST package. A smoke test drives the analyzer with sqlc's own PostgreSQL parser to prove the repointed analyzer resolves columns, types, star expansion, and aliases end-to-end against internal/sql/ast. This is the first step of merging xqlc back into sqlc as the future analysis core; ClickHouse will be the first engine wired onto it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * clickhouse: analyze queries through the core catalog + analyzer Wire ClickHouse onto the merged core: a dialect seed registering the built-in ClickHouse types, and a DDL handler that populates the core catalog from CREATE TABLE using sqlc's existing ClickHouse parser. A smoke test proves the full vertical path — ClickHouse SQL -> sqlc's ClickHouse parser -> internal/sql/ast -> core catalog + analyzer -> PrepareResult — resolving column names, types, nullability, source bindings, and star expansion, with none of the legacy compiler analyze step involved. Also fix the ClickHouse converter to render nested type parameters (the inner type of Nullable(T)/Array(T), Decimal precision, etc.) into TypeName.Name instead of dropping them as TODO nodes, so wrapped types resolve to their effective scalar type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * clickhouse: wire end-to-end sqlc generate onto the core Add the EngineClickHouse engine and a dedicated compile path so `sqlc generate` produces Go for ClickHouse entirely through the xqlc core, bypassing the legacy compiler analyze step and the in-memory sql/catalog: - config: add the "clickhouse" engine constant. - compiler: NewCompiler builds a core.Catalog seeded with the ClickHouse dialect; parseCatalog applies schema DDL to it; a new parseQueryCore resolves each query's columns and parameters via core/analyzer and assembles *compiler.Query, reusing only the shared query-metadata parsing. The legacy analyzeQuery/inferQuery/outputColumns path and the analyzer.Analyzer seam are never entered. - codegen: project the core catalog into plugin.Catalog for model/enum generation, and add a ClickHouse -> Go type map (Nullable(T) -> *T, the integer ladder, Float32/64, String, DateTime -> time.Time, ...). - clickhouse parser: compute statement byte-spans with a running offset and a semicolon scan (doubleclick reports statement starts but not ends), so leading "-- name:" annotations fall inside each statement. An endtoend case (clickhouse_select) exercises the full pipeline and its golden Go output is committed. Updating parse_basic/clickhouse's golden reflects the corrected statement spans and now-detected query name/cmd. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * core: generate catalog accessors with sqlc (sqlc building sqlc) Introduce internal/core/catalogdb, a sqlc-generated SQLite query layer over the core catalog's own sql_* tables — sqlc analyzing sqlc's catalog. The catalog's schema.sql is the sqlc schema; catalogdb/query.sql holds the queries; //go:generate runs sqlc against them. Generation is offline and the output is committed, so there is no build-time cycle, and because the SQLite engine runs on the legacy compiler (not the core catalog) there is no analysis recursion. Vertical slice: types.go (CreateType/TypeOID/TypeName/LookupType) now delegates to the generated Queries. FindProcs is split into two generated queries — FindProcsAnyNamespace and FindProcsInNamespaces (sqlc.slice) — with Go choosing based on whether namespaces were supplied, replacing the hand-built IN-list. Remaining core files still use raw SQL and will be swept over incrementally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * core: migrate all catalog queries to sqlc-generated code Sweep the remaining hand-written SQL against the sql_* catalog tables onto the generated catalogdb layer, completing the conversion started in the types.go/FindProcs slice. Namespace, dialect, type, class, attribute, constraint, proc, operator, and cast queries now all run through catalogdb.Queries. Notable rewrites: - FindOperators: the conditionally-appended type filters become a single static query using (@arg = 0 OR col = @arg) sentinels. - Nullable OID columns round-trip through sql.NullInt64 (orZero maps NULL to the 0 sentinel the Go API uses). - New typed catalog accessors back the cross-package call sites that previously reached into the raw handle: DropClass (ClickHouse DROP TABLE), ClassColumns (analyzer scope), and Namespaces / TablesInNamespace / ClassCodegenColumns (codegen catalog projection). Only the schema bootstrap (db.Exec(ddl)) and the DB()/Close() handles remain as raw database/sql; every catalog query is now sqlc-generated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * core: move catalog SQL into a catalogdef package, export Schema Extract schema.sql and query.sql from internal/core (and catalogdb) into a dedicated internal/core/catalogdef package that owns the SQL defining sqlc's catalog and embeds the schema (catalogdef.Schema). sqlc reads its schema and queries from catalogdef and still generates the querier into catalogdb, keeping SQL sources separate from generated code. core/catalog.go now builds its in-memory catalog from catalogdef.Schema instead of embedding schema.sql itself, so the DDL used at runtime and the schema fed to codegen are one source of truth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * clickhouse/core: strip comments and remove xqlc references Remove comments from the Go files authored for this work (catalog core, analyzer, ClickHouse engine, compile path, codegen type map) and drop the comments added to the files touched along the way, keeping //go:generate and //go:embed directives. No remaining references to xqlc. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTGxNHW6v1S1YyC9FDSgrK * core: address review — move shim/DDL apply out of engine and compiler Move pluginCatalogFromCore into a new internal/core/shim package as PluginCatalog. Move the engine-neutral DDL apply into a new internal/core/schema package and have the compiler call it directly, so no clickhouse-specific code remains in the schema loading loop. Push the ClickHouse type unwrapping into the engine's column conversion so the core schema apply only sees canonical scalar types. Rename the core compile path file to parse_core.go. Remove the unit tests in favor of end-to-end coverage. * clickhouse: expose core analysis via `sqlc analyze`, drop codegen Wire the ClickHouse engine into `sqlc analyze` so the core catalog and analyzer can be exercised end to end without committing to code generation yet. The analyze command builds the compiler, applies the schema DDL to the core catalog, and reports inferred result columns as JSON — the same static-analysis path `generate` would use. Remove the not-yet-ready code generation surface: the ClickHouse Go type map, the core-catalog to plugin.Catalog projection, and the generate golden test. ClickHouse type inference is now covered by an analyze_basic end-to-end case, matching how the other engines are tested. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9f362b9 commit af1016f

40 files changed

Lines changed: 3728 additions & 28 deletions

docs/howto/analyze.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ provided. The schema is always read from the `--schema` file.
1818

1919
## Flags
2020

21-
- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`, or
22-
`sqlite`. Required.
21+
- `--dialect`, `-d` - The SQL dialect to use. One of `postgresql`, `mysql`,
22+
`sqlite`, or `clickhouse`. Required.
2323
- `--schema`, `-s` - Path to the schema (DDL) file. Required.
2424
- `--ast` - Include each statement's AST in the output. Defaults to `false`.
2525

go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,24 @@ require (
3030
google.golang.org/grpc v1.83.0
3131
google.golang.org/protobuf v1.36.11
3232
gopkg.in/yaml.v3 v3.0.1
33+
modernc.org/sqlite v1.55.0
3334
)
3435

3536
require (
3637
cel.dev/expr v0.25.2 // indirect
3738
filippo.io/edwards25519 v1.2.0 // indirect
39+
github.com/dustin/go-humanize v1.0.1 // indirect
40+
github.com/google/uuid v1.6.0 // indirect
3841
github.com/inconshreveable/mousetrap v1.1.0 // indirect
3942
github.com/jackc/pgpassfile v1.0.0 // indirect
4043
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
4144
github.com/jackc/puddle/v2 v2.2.2 // indirect
4245
github.com/kr/text v0.2.0 // indirect
46+
github.com/mattn/go-isatty v0.0.20 // indirect
4347
github.com/ncruces/go-sqlite3-wasm/v3 v3.2.35303 // indirect
48+
github.com/ncruces/go-strftime v1.0.0 // indirect
4449
github.com/ncruces/julianday v1.0.0 // indirect
50+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
4551
github.com/rogpeppe/go-internal v1.10.0 // indirect
4652
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
4753
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
@@ -53,4 +59,7 @@ require (
5359
golang.org/x/text v0.39.0 // indirect
5460
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
5561
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
62+
modernc.org/libc v1.74.1 // indirect
63+
modernc.org/mathutil v1.7.1 // indirect
64+
modernc.org/memory v1.11.0 // indirect
5665
)

go.sum

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYY
1313
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1414
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1515
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
16+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
17+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
1618
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
1719
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
1820
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -27,8 +29,12 @@ github.com/google/cel-go v0.30.0 h1:ll54AkzKunWkBn9wSoiUXbFZXYZTkdJGNXTBXUoolGo=
2729
github.com/google/cel-go v0.30.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
2830
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
2931
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
32+
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
33+
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
3034
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
3135
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
36+
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
37+
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
3238
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
3339
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
3440
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -47,16 +53,22 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
4753
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
4854
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
4955
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
56+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
57+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
5058
github.com/ncruces/go-sqlite3 v0.35.2 h1:YOoumI7tkxMIm1MBrkucRb1qtvAPEh8RrZtv6U+2aLs=
5159
github.com/ncruces/go-sqlite3 v0.35.2/go.mod h1:lVlozMCF6VGdI1FOgl0pBfMviw6DIh/QIMKQyjmg2ac=
5260
github.com/ncruces/go-sqlite3-wasm/v3 v3.2.35303 h1:td8kMW1bWwzc7NnlzPjQV4GbDNLkje8htGBfbZRaSh8=
5361
github.com/ncruces/go-sqlite3-wasm/v3 v3.2.35303/go.mod h1:o8gr9w/50fXA5TDskg6bNUjvqmFfw4KaXth4q+yDSjg=
62+
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
63+
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
5464
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
5565
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
5666
github.com/pganalyze/pg_query_go/v6 v6.2.2 h1:O0L6zMC226R82RF3X5n0Ki6HjytDsoAzuzp4ATVAHNo=
5767
github.com/pganalyze/pg_query_go/v6 v6.2.2/go.mod h1:Cn6+j4870kJz3iYNsb0VsNG04vpSWgEvBwc590J4qD0=
5868
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5969
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
70+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
71+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
6072
github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ=
6173
github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8=
6274
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -106,14 +118,19 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
106118
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
107119
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
108120
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
121+
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
122+
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
109123
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
110124
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
111125
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
112126
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
127+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
113128
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
114129
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
115130
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
116131
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
132+
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
133+
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
117134
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
118135
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
119136
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
@@ -130,3 +147,31 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
130147
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
131148
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
132149
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
150+
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
151+
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
152+
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
153+
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
154+
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
155+
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
156+
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
157+
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
158+
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
159+
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
160+
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
161+
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
162+
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
163+
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
164+
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
165+
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
166+
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
167+
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
168+
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
169+
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
170+
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
171+
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
172+
modernc.org/sqlite v1.55.0 h1:hIFh0MCH0rGinQ/4KYb5/UbCkRkb+UP+OkLCVWa5MTM=
173+
modernc.org/sqlite v1.55.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
174+
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
175+
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
176+
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
177+
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

internal/cmd/analyze.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ Examples:
3737
# Analyze a SQLite query
3838
sqlc analyze --dialect sqlite --schema schema.sql query.sql
3939
40+
# Analyze a ClickHouse query
41+
sqlc analyze --dialect clickhouse --schema schema.sql query.sql
42+
4043
# Analyze a query piped via stdin
4144
echo "-- name: GetAuthor :one
4245
SELECT * FROM authors WHERE id = $1;" | sqlc analyze --dialect postgresql --schema schema.sql
@@ -50,7 +53,7 @@ Examples:
5053
return err
5154
}
5255
if dialect == "" {
53-
return fmt.Errorf("--dialect flag is required (postgresql, mysql, or sqlite)")
56+
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, or clickhouse)")
5457
}
5558

5659
schemaPath, err := cmd.Flags().GetString("schema")
@@ -107,8 +110,10 @@ Examples:
107110
engine = config.EngineMySQL
108111
case "sqlite":
109112
engine = config.EngineSQLite
113+
case "clickhouse":
114+
engine = config.EngineClickHouse
110115
default:
111-
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, or sqlite)", dialect)
116+
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, or clickhouse)", dialect)
112117
}
113118

114119
sql := config.SQL{
@@ -150,7 +155,7 @@ Examples:
150155
return nil
151156
},
152157
}
153-
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, or sqlite)")
158+
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, or clickhouse)")
154159
cmd.Flags().StringP("schema", "s", "", "path to the schema file")
155160
cmd.Flags().BoolP("ast", "", false, "include the statement AST in the output")
156161
return cmd

internal/compiler/compile.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"path/filepath"
1010
"strings"
1111

12+
coreschema "github.com/sqlc-dev/sqlc/internal/core/schema"
1213
"github.com/sqlc-dev/sqlc/internal/migrations"
1314
"github.com/sqlc-dev/sqlc/internal/multierr"
1415
"github.com/sqlc-dev/sqlc/internal/opts"
@@ -55,6 +56,16 @@ func (c *Compiler) parseCatalog(schemas []string) error {
5556
continue
5657
}
5758

59+
if c.coreCatalog != nil {
60+
for i := range stmts {
61+
if err := coreschema.Apply(c.coreCatalog, stmts[i].Raw); err != nil {
62+
merr.Add(filename, contents, stmts[i].Pos(), err)
63+
continue
64+
}
65+
}
66+
continue
67+
}
68+
5869
for i := range stmts {
5970
if err := c.catalog.Update(stmts[i], c); err != nil {
6071
merr.Add(filename, contents, stmts[i].Pos(), err)

internal/compiler/engine.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66

77
"github.com/sqlc-dev/sqlc/internal/analyzer"
88
"github.com/sqlc-dev/sqlc/internal/config"
9+
"github.com/sqlc-dev/sqlc/internal/core"
910
"github.com/sqlc-dev/sqlc/internal/dbmanager"
11+
"github.com/sqlc-dev/sqlc/internal/engine/clickhouse"
1012
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
1113
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
1214
pganalyze "github.com/sqlc-dev/sqlc/internal/engine/postgresql/analyzer"
@@ -27,6 +29,8 @@ type Compiler struct {
2729
client dbmanager.Client
2830
selector selector
2931

32+
coreCatalog *core.Catalog
33+
3034
schema []string
3135

3236
// databaseOnlyMode indicates that the compiler should use database-only analysis
@@ -111,6 +115,14 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings, parserOpts opts
111115
)
112116
}
113117
}
118+
case config.EngineClickHouse:
119+
c.parser = clickhouse.NewParser()
120+
c.selector = newDefaultSelector()
121+
cat, err := core.New(clickhouse.Dialect())
122+
if err != nil {
123+
return nil, fmt.Errorf("clickhouse: init catalog: %w", err)
124+
}
125+
c.coreCatalog = cat
114126
default:
115127
return nil, fmt.Errorf("unknown engine: %s", conf.Engine)
116128
}
@@ -145,4 +157,7 @@ func (c *Compiler) Close(ctx context.Context) {
145157
if c.client != nil {
146158
c.client.Close(ctx)
147159
}
160+
if c.coreCatalog != nil {
161+
c.coreCatalog.Close()
162+
}
148163
}

internal/compiler/parse.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import (
1919
var debugDumpAST = sqlcdebug.New("dumpast")
2020

2121
func (c *Compiler) parseQuery(stmt ast.Node, src string, o opts.Parser) (*Query, error) {
22+
if c.coreCatalog != nil {
23+
return c.parseQueryCore(stmt, src)
24+
}
25+
2226
ctx := context.Background()
2327

2428
if debugDumpAST.Value() == "1" {

internal/compiler/parse_core.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package compiler
2+
3+
import (
4+
"errors"
5+
"strings"
6+
7+
"github.com/sqlc-dev/sqlc/internal/core"
8+
coreanalyzer "github.com/sqlc-dev/sqlc/internal/core/analyzer"
9+
"github.com/sqlc-dev/sqlc/internal/metadata"
10+
"github.com/sqlc-dev/sqlc/internal/source"
11+
"github.com/sqlc-dev/sqlc/internal/sql/ast"
12+
"github.com/sqlc-dev/sqlc/internal/sql/validate"
13+
)
14+
15+
func (c *Compiler) parseQueryCore(stmt ast.Node, src string) (*Query, error) {
16+
raw, ok := stmt.(*ast.RawStmt)
17+
if !ok {
18+
return nil, errors.New("node is not a statement")
19+
}
20+
rawSQL, err := source.Pluck(src, raw.StmtLocation, raw.StmtLen)
21+
if err != nil {
22+
return nil, err
23+
}
24+
if strings.TrimSpace(rawSQL) == "" {
25+
return nil, errors.New("missing semicolon at end of file")
26+
}
27+
28+
name, cmd, err := metadata.ParseQueryNameAndType(rawSQL, metadata.CommentSyntax(c.parser.CommentSyntax()))
29+
if err != nil {
30+
return nil, err
31+
}
32+
if name == "" {
33+
return nil, nil
34+
}
35+
if err := validate.Cmd(raw.Stmt, name, cmd); err != nil {
36+
return nil, err
37+
}
38+
39+
md := metadata.Metadata{Name: name, Cmd: cmd}
40+
cleanedComments, err := source.CleanedComments(rawSQL, c.parser.CommentSyntax())
41+
if err != nil {
42+
return nil, err
43+
}
44+
md.Params, md.Flags, md.RuleSkiplist, err = metadata.ParseCommentFlags(cleanedComments)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
var cols []*Column
50+
var params []Parameter
51+
if _, ok := raw.Stmt.(*ast.SelectStmt); ok {
52+
res, err := coreanalyzer.Prepare(c.coreCatalog, raw)
53+
if err != nil {
54+
return nil, err
55+
}
56+
for _, col := range res.Columns {
57+
cols = append(cols, coreColumn(col))
58+
}
59+
for _, p := range res.Parameters {
60+
params = append(params, Parameter{Number: p.Number, Column: coreParamColumn(p)})
61+
}
62+
}
63+
64+
trimmed, comments, err := source.StripComments(rawSQL)
65+
if err != nil {
66+
return nil, err
67+
}
68+
md.Comments = comments
69+
70+
var insertTable *ast.TableName
71+
if ins, ok := raw.Stmt.(*ast.InsertStmt); ok {
72+
insertTable, _ = ParseTableName(ins.Relation)
73+
}
74+
75+
return &Query{
76+
RawStmt: raw,
77+
Metadata: md,
78+
Params: params,
79+
Columns: cols,
80+
SQL: trimmed,
81+
InsertIntoTable: insertTable,
82+
}, nil
83+
}
84+
85+
func coreColumn(c core.Column) *Column {
86+
col := &Column{
87+
Name: c.Name,
88+
DataType: c.DataType,
89+
NotNull: c.NotNull,
90+
}
91+
if c.Source != nil && c.Source.Table != "" {
92+
col.Table = &ast.TableName{Schema: c.Source.Schema, Name: c.Source.Table}
93+
col.TableAlias = c.Source.TableAlias
94+
col.OriginalName = c.Source.Column
95+
}
96+
if c.TypeLength > 0 {
97+
l := c.TypeLength
98+
col.Length = &l
99+
}
100+
return col
101+
}
102+
103+
func coreParamColumn(p core.Parameter) *Column {
104+
col := &Column{
105+
Name: p.Name,
106+
DataType: p.DataType,
107+
NotNull: p.NotNull,
108+
}
109+
if p.Source != nil && p.Source.Table != "" {
110+
col.Table = &ast.TableName{Schema: p.Source.Schema, Name: p.Source.Table}
111+
col.OriginalName = p.Source.Column
112+
}
113+
return col
114+
}

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const (
5454
EngineMySQL Engine = "mysql"
5555
EnginePostgreSQL Engine = "postgresql"
5656
EngineSQLite Engine = "sqlite"
57+
EngineClickHouse Engine = "clickhouse"
5758
)
5859

5960
type Config struct {

0 commit comments

Comments
 (0)