Skip to content

Commit 0bd9f5f

Browse files
kyleconroyclaude
andauthored
Add coreanalyzer experiment routing generate through the analysis core (#4544)
* Add coreanalyzer experiment routing generate through the analysis core SQLCEXPERIMENT=coreanalyzer builds each query set's compiler with WithCoreAnalysis, the same path sqlc analyze and the ClickHouse and GoogleSQL engines already use. The experiment now actually reaches the compiler: processQuerySets threads it into the parser options, which were previously always empty. Generating through the core also needs a catalog for codegen to build models from, which the core path never carried: its Result held a nil catalog that pluginCatalog would dereference. The core catalog is now dumped into the legacy catalog shape after the schema is applied. Array columns additionally set ArrayDims, which codegen renders a [] per, so they come out as slices rather than silently losing their array-ness. An end-to-end case pins the generated output for postgresql, sqlite and mysql. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANVHEs41RJbybS3ja9N1SA * Drop the comment on the experiment threading in processQuerySets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANVHEs41RJbybS3ja9N1SA * Thread the whole experiment struct now that coreanalyzer is the only one Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANVHEs41RJbybS3ja9N1SA --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b0a3e4b commit 0bd9f5f

29 files changed

Lines changed: 683 additions & 7 deletions

docs/reference/environment-variables.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@ SQLCEXPERIMENT=nofoo # explicitly disable foo experiment
1414
SQLCEXPERIMENT=foo,nobar # enable foo, disable bar
1515
```
1616

17-
Currently, no experiments are defined. Experiments will be documented here as
18-
they are introduced.
17+
The following experiments are defined:
18+
19+
### coreanalyzer
20+
21+
Routes `sqlc generate` through the core catalog and analyzer instead of each
22+
engine's own analysis path. This is the same analysis used by `sqlc analyze`,
23+
and the only analysis path for the ClickHouse and GoogleSQL engines.
24+
25+
```
26+
SQLCEXPERIMENT=coreanalyzer
27+
```
1928

2029
## SQLCCACHE
2130

internal/cmd/generate.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,11 @@ func (g *generator) ProcessResult(ctx context.Context, combo config.CombinedSett
254254

255255
func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) {
256256
defer trace.StartRegion(ctx, "parse").End()
257-
c, err := compiler.NewCompiler(sql, combo, parserOpts)
257+
var copts []compiler.Option
258+
if parserOpts.Experiment.CoreAnalyzer {
259+
copts = append(copts, compiler.WithCoreAnalysis())
260+
}
261+
c, err := compiler.NewCompiler(sql, combo, parserOpts, copts...)
258262
defer func() {
259263
if c != nil {
260264
c.Close(ctx)

internal/cmd/process.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ func processQuerySets(ctx context.Context, rp ResultProcessor, conf *config.Conf
8686
sql.Queries = joined
8787

8888
var name, lang string
89-
parseOpts := opts.Parser{}
89+
parseOpts := opts.Parser{
90+
Experiment: o.Env.Experiment,
91+
}
9092

9193
switch {
9294
case sql.Gen.Go != nil:

internal/compiler/catalog_core.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package compiler
2+
3+
import (
4+
"strings"
5+
6+
"github.com/sqlc-dev/sqlc/internal/core"
7+
"github.com/sqlc-dev/sqlc/internal/sql/ast"
8+
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
9+
)
10+
11+
// coreResultCatalog dumps the core catalog into the legacy catalog shape a
12+
// Result carries, so codegen sees the same table models either way a query
13+
// set was analyzed. Only relations make the trip: codegen reads tables and
14+
// their columns to build models, and none of the types, functions or
15+
// operators the core catalog also holds.
16+
func coreResultCatalog(c *core.Catalog) (*catalog.Catalog, error) {
17+
cat := catalog.New("public")
18+
namespaces, err := c.Namespaces()
19+
if err != nil {
20+
return nil, err
21+
}
22+
for _, ns := range namespaces {
23+
schema := &catalog.Schema{Name: ns.Name}
24+
tables, err := c.TablesInNamespace(ns.OID)
25+
if err != nil {
26+
return nil, err
27+
}
28+
for _, table := range tables {
29+
cols, err := c.ClassCodegenColumns(table.OID)
30+
if err != nil {
31+
return nil, err
32+
}
33+
t := &catalog.Table{Rel: &ast.TableName{Schema: ns.Name, Name: table.Name}}
34+
for _, col := range cols {
35+
// The catalog names an array type after its element with the
36+
// suffix appended, which is codegen's data type and array
37+
// flag in one string. The core catalog holds one dimension,
38+
// and codegen renders a "[]" per dimension.
39+
dataType, isArray := strings.CutSuffix(col.TypeName, core.ArraySuffix)
40+
column := &catalog.Column{
41+
Name: col.Name,
42+
Type: ast.TypeName{Name: dataType},
43+
IsNotNull: col.NotNull,
44+
IsArray: isArray,
45+
}
46+
if isArray {
47+
column.ArrayDims = 1
48+
}
49+
t.Columns = append(t.Columns, column)
50+
}
51+
schema.Tables = append(schema.Tables, t)
52+
}
53+
cat.Schemas = append(cat.Schemas, schema)
54+
}
55+
return cat, nil
56+
}

internal/compiler/compile.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ func (c *Compiler) parseCatalogCore(files []schemaFile, merr *multierr.Error) er
142142
}
143143
// Whatever apply reported is already in merr, which the caller returns.
144144
c.coreCatalog = cat
145+
146+
// Codegen consumes the catalog through the Result, in the legacy shape.
147+
legacy, err := coreResultCatalog(cat)
148+
if err != nil {
149+
return fmt.Errorf("%s: dump catalog: %w", c.conf.Engine, err)
150+
}
151+
c.catalog = legacy
145152
return nil
146153
}
147154

internal/compiler/parse_core.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func coreColumn(c core.Column) *Column {
9494
NotNull: c.NotNull,
9595
IsArray: c.IsArray,
9696
}
97+
// The core reports arrays without dimensions, and codegen renders one
98+
// "[]" per dimension.
99+
if c.IsArray {
100+
col.ArrayDims = 1
101+
}
97102
if c.Source != nil && c.Source.Table != "" {
98103
col.Table = &ast.TableName{Schema: c.Source.Schema, Name: c.Source.Table}
99104
col.TableAlias = c.Source.TableAlias
@@ -113,6 +118,9 @@ func coreParamColumn(p core.Parameter, params *named.ParamSet) *Column {
113118
NotNull: p.NotNull,
114119
IsArray: p.IsArray,
115120
}
121+
if p.IsArray {
122+
col.ArrayDims = 1
123+
}
116124
if p.Source != nil && p.Source.Table != "" {
117125
col.Table = &ast.TableName{Schema: p.Source.Schema, Name: p.Source.Table}
118126
col.OriginalName = p.Source.Column
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"command": "generate",
3+
"env": {
4+
"SQLCEXPERIMENT": "coreanalyzer"
5+
}
6+
}

internal/endtoend/testdata/experiment_coreanalyzer/mysql/go/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/experiment_coreanalyzer/mysql/go/models.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/experiment_coreanalyzer/mysql/go/query.sql.go

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)