Files
upstream-mirror 0f4df23290
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

200 lines
5.8 KiB
Go

package compiler
import (
"context"
"fmt"
"github.com/sqlc-dev/sqlc/internal/analyzer"
"github.com/sqlc-dev/sqlc/internal/config"
"github.com/sqlc-dev/sqlc/internal/core"
"github.com/sqlc-dev/sqlc/internal/dbmanager"
"github.com/sqlc-dev/sqlc/internal/engine/clickhouse"
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
"github.com/sqlc-dev/sqlc/internal/engine/duckdb"
"github.com/sqlc-dev/sqlc/internal/engine/googlesql"
"github.com/sqlc-dev/sqlc/internal/engine/mssql"
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
pganalyze "github.com/sqlc-dev/sqlc/internal/engine/postgresql/analyzer"
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
sqliteanalyze "github.com/sqlc-dev/sqlc/internal/engine/sqlite/analyzer"
"github.com/sqlc-dev/sqlc/internal/opts"
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
)
type Compiler struct {
conf config.SQL
combo config.CombinedSettings
catalog *catalog.Catalog
parser Parser
result *Result
analyzer analyzer.Analyzer
client dbmanager.Client
selector selector
coreCatalog *core.Catalog
// coreAnalysis routes every engine through the core catalog and analyzer,
// and coreDialect seeds the catalog it does so with. The catalog itself is
// not built until the schema is loaded, because the two are cached
// together.
coreAnalysis bool
coreDialect core.Option
// newParser builds a parser for the configured engine, and is set for
// every engine either path supports. The core path analyzes statements
// concurrently, and a parser holds enough state that two goroutines cannot
// share one, so a goroutine that has to parse something of its own — the
// query text it just rewrote — builds its own rather than taking c.parser.
newParser func() Parser
schema []string
}
// Option configures a Compiler.
type Option func(*Compiler)
// WithCoreAnalysis analyzes queries with the core catalog and analyzer rather
// than each engine's own analysis path. ClickHouse and GoogleSQL always do;
// this opts the remaining engines in.
func WithCoreAnalysis() Option {
return func(c *Compiler) { c.coreAnalysis = true }
}
func NewCompiler(conf config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, options ...Option) (*Compiler, error) {
c := &Compiler{conf: conf, combo: combo}
for _, o := range options {
o(c)
}
// ClickHouse, GoogleSQL, SQL Server and DuckDB have no legacy analysis
// path to fall back to.
switch conf.Engine {
case config.EngineClickHouse, config.EngineGoogleSQL, config.EngineMSSQL, config.EngineDuckDB:
c.coreAnalysis = true
}
if c.coreAnalysis {
if err := c.initCore(); err != nil {
return nil, err
}
return c, nil
}
if conf.Database != nil && conf.Database.Managed {
client := dbmanager.NewClient(combo.Global.Servers)
c.client = client
}
switch conf.Engine {
case config.EngineSQLite:
c.newParser = func() Parser { return sqlite.NewParser() }
c.catalog = sqlite.NewCatalog()
c.selector = newSQLiteSelector()
if conf.Database != nil {
if conf.Analyzer.Database == nil || *conf.Analyzer.Database {
c.analyzer = analyzer.Cached(
sqliteanalyze.New(*conf.Database),
combo.Global,
*conf.Database,
)
}
}
case config.EngineMySQL:
c.newParser = func() Parser { return dolphin.NewParser() }
c.catalog = dolphin.NewCatalog()
c.selector = newDefaultSelector()
case config.EnginePostgreSQL:
c.newParser = func() Parser { return postgresql.NewParser() }
c.catalog = postgresql.NewCatalog()
c.selector = newDefaultSelector()
if conf.Database != nil {
if conf.Analyzer.Database == nil || *conf.Analyzer.Database {
c.analyzer = analyzer.Cached(
pganalyze.New(c.client, *conf.Database),
combo.Global,
*conf.Database,
)
}
}
default:
return nil, fmt.Errorf("unknown engine: %s", conf.Engine)
}
c.parser = c.newParser()
return c, nil
}
// initCore wires up the engine's parser and a core catalog seeded with its
// dialect. No legacy catalog and no analyzer connection are involved.
func (c *Compiler) initCore() error {
var dialect core.Option
switch c.conf.Engine {
case config.EngineSQLite:
c.newParser = func() Parser { return sqlite.NewParser() }
c.selector = newSQLiteSelector()
dialect = sqlite.Dialect()
case config.EngineMySQL:
c.newParser = func() Parser { return dolphin.NewParser() }
c.selector = newDefaultSelector()
dialect = dolphin.Dialect()
case config.EnginePostgreSQL:
c.newParser = func() Parser { return postgresql.NewParser() }
c.selector = newDefaultSelector()
dialect = postgresql.Dialect()
case config.EngineClickHouse:
c.newParser = func() Parser { return clickhouse.NewParser() }
c.selector = newDefaultSelector()
dialect = clickhouse.Dialect()
case config.EngineGoogleSQL:
c.newParser = func() Parser { return googlesql.NewParser() }
c.selector = newDefaultSelector()
dialect = googlesql.Dialect()
case config.EngineMSSQL:
c.newParser = func() Parser { return mssql.NewParser() }
c.selector = newDefaultSelector()
dialect = mssql.Dialect()
case config.EngineDuckDB:
c.newParser = func() Parser { return duckdb.NewParser() }
c.selector = newDefaultSelector()
dialect = duckdb.Dialect()
default:
return fmt.Errorf("unknown engine: %s", c.conf.Engine)
}
c.parser = c.newParser()
c.coreDialect = dialect
return nil
}
func (c *Compiler) Catalog() *catalog.Catalog {
return c.catalog
}
func (c *Compiler) ParseCatalog(schema []string) error {
return c.parseCatalog(schema)
}
func (c *Compiler) ParseQueries(queries []string, o opts.Parser) error {
r, err := c.parseQueries(o)
if err != nil {
return err
}
c.result = r
return nil
}
func (c *Compiler) Result() *Result {
return c.result
}
func (c *Compiler) Close(ctx context.Context) {
if c.analyzer != nil {
c.analyzer.Close(ctx)
}
if c.client != nil {
c.client.Close(ctx)
}
if c.coreCatalog != nil {
c.coreCatalog.Close()
}
}