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
200 lines
5.8 KiB
Go
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()
|
|
}
|
|
}
|