snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
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
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
This commit is contained in:
233
internal/engine/sqlite/analyzer/analyze.go
Normal file
233
internal/engine/sqlite/analyzer/analyze.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
|
||||
core "github.com/sqlc-dev/sqlc/internal/analysis"
|
||||
"github.com/sqlc-dev/sqlc/internal/config"
|
||||
"github.com/sqlc-dev/sqlc/internal/shfmt"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/named"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/sqlerr"
|
||||
"github.com/sqlc-dev/sqlc/internal/sqlcdebug"
|
||||
_ "github.com/sqlc-dev/sqlc/internal/sqlite3ext"
|
||||
)
|
||||
|
||||
var debugDatabases = sqlcdebug.New("databases")
|
||||
|
||||
type Analyzer struct {
|
||||
db config.Database
|
||||
conn *sqlite3.Conn
|
||||
replacer *shfmt.Replacer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func New(db config.Database) *Analyzer {
|
||||
return &Analyzer{
|
||||
db: db,
|
||||
replacer: shfmt.NewReplacer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Analyzer) Analyze(ctx context.Context, n ast.Node, query string, migrations []string, ps *named.ParamSet) (*core.Analysis, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
if a.conn == nil {
|
||||
var uri string
|
||||
applyMigrations := a.db.Managed
|
||||
if a.db.Managed {
|
||||
// For managed databases, create an in-memory database
|
||||
uri = ":memory:"
|
||||
} else if debugDatabases.Value() == "managed" {
|
||||
return nil, fmt.Errorf("database: connections disabled via SQLCDEBUG=databases=managed")
|
||||
} else {
|
||||
uri = a.replacer.Replace(a.db.URI)
|
||||
// For in-memory databases, we need to apply migrations since the database starts empty
|
||||
if isInMemoryDatabase(uri) {
|
||||
applyMigrations = true
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := sqlite3.Open(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open sqlite database: %w", err)
|
||||
}
|
||||
a.conn = conn
|
||||
|
||||
// Apply migrations for managed or in-memory databases
|
||||
if applyMigrations {
|
||||
for _, m := range migrations {
|
||||
if len(strings.TrimSpace(m)) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := a.conn.Exec(m); err != nil {
|
||||
a.conn.Close()
|
||||
a.conn = nil
|
||||
return nil, fmt.Errorf("migration failed: %s: %w", m, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the statement to get column and parameter information
|
||||
stmt, _, err := a.conn.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, a.extractSqlErr(n, err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var result core.Analysis
|
||||
|
||||
// Get column information
|
||||
colCount := stmt.ColumnCount()
|
||||
for i := range colCount {
|
||||
name := stmt.ColumnName(i)
|
||||
declType := stmt.ColumnDeclType(i)
|
||||
dbName := stmt.ColumnDatabaseName(i)
|
||||
tableName := stmt.ColumnTableName(i)
|
||||
originName := stmt.ColumnOriginName(i)
|
||||
|
||||
// Determine if column is NOT NULL
|
||||
var notNull bool
|
||||
var dataType string
|
||||
if originName != "" {
|
||||
declType, _, notNull, _, _, _ = a.conn.TableColumnMetadata(
|
||||
dbName, tableName, originName)
|
||||
}
|
||||
|
||||
// Normalize the data type
|
||||
dataType = normalizeType(declType)
|
||||
|
||||
col := &core.Column{
|
||||
Name: name,
|
||||
OriginalName: originName,
|
||||
DataType: dataType,
|
||||
NotNull: notNull,
|
||||
}
|
||||
|
||||
if tableName != "" {
|
||||
col.Table = &core.Identifier{
|
||||
Schema: dbName,
|
||||
Name: tableName,
|
||||
}
|
||||
}
|
||||
|
||||
result.Columns = append(result.Columns, col)
|
||||
}
|
||||
|
||||
// Get parameter information
|
||||
bindCount := stmt.BindCount()
|
||||
for i := 1; i <= bindCount; i++ {
|
||||
paramName := stmt.BindName(i)
|
||||
|
||||
// SQLite doesn't provide parameter types from prepared statements
|
||||
// We use "any" as the default type
|
||||
name := ""
|
||||
if paramName != "" {
|
||||
// Remove the prefix (?, :, @, $) from parameter names
|
||||
name = strings.TrimLeft(paramName, "?:@$")
|
||||
}
|
||||
if ps != nil {
|
||||
if n, ok := ps.NameFor(i); ok {
|
||||
name = n
|
||||
}
|
||||
}
|
||||
|
||||
result.Params = append(result.Params, &core.Parameter{
|
||||
Number: int32(i),
|
||||
Column: &core.Column{
|
||||
Name: name,
|
||||
DataType: "any",
|
||||
NotNull: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a *Analyzer) extractSqlErr(n ast.Node, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// Try to extract SQLite error details
|
||||
var sqliteErr *sqlite3.Error
|
||||
if e, ok := err.(*sqlite3.Error); ok {
|
||||
sqliteErr = e
|
||||
}
|
||||
if sqliteErr != nil {
|
||||
return &sqlerr.Error{
|
||||
Code: fmt.Sprintf("%d", sqliteErr.Code()),
|
||||
Message: sqliteErr.Error(),
|
||||
Location: n.Pos(),
|
||||
}
|
||||
}
|
||||
return &sqlerr.Error{
|
||||
Message: err.Error(),
|
||||
Location: n.Pos(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Analyzer) Close(_ context.Context) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a.conn != nil {
|
||||
err := a.conn.Close()
|
||||
a.conn = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isInMemoryDatabase checks if a SQLite URI refers to an in-memory database
|
||||
func isInMemoryDatabase(uri string) bool {
|
||||
if uri == ":memory:" || uri == "" {
|
||||
return true
|
||||
}
|
||||
// Check for file URI with mode=memory parameter
|
||||
// e.g., "file:test?mode=memory&cache=shared"
|
||||
if strings.Contains(uri, "mode=memory") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// normalizeType converts SQLite type declarations to standard type names
|
||||
func normalizeType(declType string) string {
|
||||
if declType == "" {
|
||||
return "any"
|
||||
}
|
||||
|
||||
// Convert to lowercase for comparison
|
||||
lower := strings.ToLower(declType)
|
||||
|
||||
// SQLite type affinity rules (https://www.sqlite.org/datatype3.html)
|
||||
switch {
|
||||
case strings.Contains(lower, "int"):
|
||||
return "integer"
|
||||
case strings.Contains(lower, "char"),
|
||||
strings.Contains(lower, "clob"),
|
||||
strings.Contains(lower, "text"):
|
||||
return "text"
|
||||
case strings.Contains(lower, "blob"):
|
||||
return "blob"
|
||||
case strings.Contains(lower, "real"),
|
||||
strings.Contains(lower, "floa"),
|
||||
strings.Contains(lower, "doub"):
|
||||
return "real"
|
||||
case strings.Contains(lower, "bool"):
|
||||
return "boolean"
|
||||
case strings.Contains(lower, "date"),
|
||||
strings.Contains(lower, "time"):
|
||||
return "datetime"
|
||||
default:
|
||||
// Return as-is for numeric or other types
|
||||
return lower
|
||||
}
|
||||
}
|
||||
15
internal/engine/sqlite/catalog.go
Normal file
15
internal/engine/sqlite/catalog.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package sqlite
|
||||
|
||||
import "github.com/sqlc-dev/sqlc/internal/sql/catalog"
|
||||
|
||||
func NewCatalog() *catalog.Catalog {
|
||||
def := "main"
|
||||
return &catalog.Catalog{
|
||||
DefaultSchema: def,
|
||||
Schemas: []*catalog.Schema{
|
||||
defaultSchema(def),
|
||||
},
|
||||
LoadExtension: loadExtension,
|
||||
Extensions: map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
1185
internal/engine/sqlite/convert.go
Normal file
1185
internal/engine/sqlite/convert.go
Normal file
File diff suppressed because it is too large
Load Diff
45
internal/engine/sqlite/dialect/dialect.json
Normal file
45
internal/engine/sqlite/dialect/dialect.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"alias": "base",
|
||||
"const": {
|
||||
"integer": "integer",
|
||||
"float": "real",
|
||||
"string": "text",
|
||||
"bool": "boolean"
|
||||
},
|
||||
"bool": "boolean",
|
||||
"comparison": ["=", "==", "<>", "!=", "<", "<=", ">", ">=", "IS", "IS NOT", "LIKE", "NOT LIKE", "GLOB", "NOT GLOB", "REGEXP", "NOT REGEXP", "MATCH", "NOT MATCH"],
|
||||
"comparison_categories": "BNSDU",
|
||||
"arithmetic": ["+", "-", "*", "/", "%"],
|
||||
"arithmetic_categories": "N",
|
||||
"cast_categories": "*",
|
||||
"modules": {
|
||||
"fts3": "enable_fts3",
|
||||
"fts4": "enable_fts3",
|
||||
"fts5": "enable_fts5",
|
||||
"geopoly": "enable_geopoly",
|
||||
"rtree": "enable_rtree",
|
||||
"rtree_i32": "enable_rtree"
|
||||
},
|
||||
"affinity": [
|
||||
{
|
||||
"contains": ["INT"],
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"contains": ["CHAR", "CLOB", "TEXT"],
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"contains": ["BLOB"],
|
||||
"type": "blob"
|
||||
},
|
||||
{
|
||||
"contains": ["REAL", "FLOA", "DOUB"],
|
||||
"type": "real"
|
||||
},
|
||||
{
|
||||
"type": "numeric"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"name":"matchinfo","args":[{"type":"any"}],"returns":"blob"}
|
||||
{"name":"matchinfo","args":[{"type":"any"},{"type":"text"}],"returns":"blob"}
|
||||
{"name":"offsets","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"optimize","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"snippet","args":[{"type":"any","has_default":true},{"type":"any","has_default":true},{"type":"text","has_default":true},{"type":"text","has_default":true},{"type":"integer","has_default":true},{"type":"integer","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
@@ -0,0 +1,7 @@
|
||||
{"name":"bm25","args":[{"type":"any","has_default":true},{"type":"real","mode":"v"}],"returns":"real"}
|
||||
{"name":"fts5_get_locale","args":[{"type":"any","has_default":true},{"type":"integer","has_default":true},{"type":"any","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"fts5_insttoken","args":[{"type":"any"}],"returns":"any"}
|
||||
{"name":"fts5_locale","args":[{"type":"text"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"fts5_source_id","returns":"text"}
|
||||
{"name":"highlight","args":[{"type":"any","has_default":true},{"type":"integer","has_default":true},{"type":"text","has_default":true},{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"snippet","args":[{"type":"any","has_default":true},{"type":"any","has_default":true},{"type":"text","has_default":true},{"type":"text","has_default":true},{"type":"integer","has_default":true},{"type":"integer","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
@@ -0,0 +1,15 @@
|
||||
{"name":"geopoly_area","args":[{"type":"any"}],"returns":"real","nullable":true}
|
||||
{"name":"geopoly_bbox","args":[{"type":"any"}],"returns":"blob","nullable":true}
|
||||
{"name":"geopoly_blob","args":[{"type":"any"}],"returns":"blob","nullable":true}
|
||||
{"name":"geopoly_ccw","args":[{"type":"any"}],"returns":"blob","nullable":true}
|
||||
{"name":"geopoly_contains_point","args":[{"type":"any"},{"type":"real"},{"type":"real"}],"returns":"integer","nullable":true}
|
||||
{"name":"geopoly_group_bbox","kind":"a","args":[{"type":"any"}],"returns":"blob","nullable":true}
|
||||
{"name":"geopoly_json","args":[{"type":"any"}],"returns":"text","nullable":true}
|
||||
{"name":"geopoly_overlap","args":[{"type":"any"},{"type":"any"}],"returns":"integer","nullable":true}
|
||||
{"name":"geopoly_regular","args":[{"type":"real"},{"type":"real"},{"type":"real"},{"type":"integer"}],"returns":"blob"}
|
||||
{"name":"geopoly_svg","args":[{"type":"text","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"geopoly_within","args":[{"type":"any"},{"type":"any"}],"returns":"integer","nullable":true}
|
||||
{"name":"geopoly_xform","args":[{"type":"any"},{"type":"real"},{"type":"real"},{"type":"real"},{"type":"real"},{"type":"real"},{"type":"real"}],"returns":"blob","nullable":true}
|
||||
{"name":"rtreecheck","args":[{"type":"text","has_default":true},{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"rtreedepth","args":[{"type":"blob"}],"returns":"integer"}
|
||||
{"name":"rtreenode","args":[{"type":"integer"},{"type":"blob"}],"returns":"text"}
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"sqlite_offset","args":[{"type":"any"}],"returns":"integer","nullable":true}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"name":"median","kind":"a","args":[{"type":"real"}],"returns":"real","nullable":true}
|
||||
{"name":"percentile","kind":"a","args":[{"type":"real"},{"type":"real"}],"returns":"real","nullable":true}
|
||||
{"name":"percentile_cont","kind":"a","args":[{"type":"real"},{"type":"real"}],"returns":"real","nullable":true}
|
||||
{"name":"percentile_disc","kind":"a","args":[{"type":"real"},{"type":"real"}],"returns":"real","nullable":true}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"name":"rtreecheck","args":[{"type":"text","has_default":true},{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"rtreedepth","args":[{"type":"blob"}],"returns":"integer"}
|
||||
{"name":"rtreenode","args":[{"type":"integer"},{"type":"blob"}],"returns":"text"}
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"soundex","args":[{"type":"text"}],"returns":"text"}
|
||||
236
internal/engine/sqlite/dialect/functions.jsonl
Normal file
236
internal/engine/sqlite/dialect/functions.jsonl
Normal file
@@ -0,0 +1,236 @@
|
||||
{"name":"-\u003e","args":[{"type":"any"},{"type":"any"}],"returns":"text","nullable":true}
|
||||
{"name":"-\u003e\u003e","args":[{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"abs","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"abs","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"int"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"tinyint"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"smallint"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"mediumint"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"bigint"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"unsigned big int"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"int2"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"int8"}],"returns":"integer"}
|
||||
{"name":"abs","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"abs","args":[{"type":"double"}],"returns":"real"}
|
||||
{"name":"abs","args":[{"type":"double precision"}],"returns":"real"}
|
||||
{"name":"abs","args":[{"type":"float"}],"returns":"real"}
|
||||
{"name":"acos","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"acosh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"asin","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"asinh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"atan","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"atan2","args":[{"type":"real"},{"type":"real"}],"returns":"real"}
|
||||
{"name":"atanh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"avg","kind":"a","args":[{"type":"any"}],"returns":"real","nullable":true}
|
||||
{"name":"ceil","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"ceil","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"int"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"tinyint"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"smallint"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"mediumint"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"bigint"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"unsigned big int"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"int2"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"int8"}],"returns":"integer"}
|
||||
{"name":"ceil","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"ceil","args":[{"type":"double"}],"returns":"real"}
|
||||
{"name":"ceil","args":[{"type":"double precision"}],"returns":"real"}
|
||||
{"name":"ceil","args":[{"type":"float"}],"returns":"real"}
|
||||
{"name":"ceiling","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"ceiling","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"int"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"tinyint"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"smallint"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"mediumint"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"bigint"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"unsigned big int"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"int2"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"int8"}],"returns":"integer"}
|
||||
{"name":"ceiling","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"ceiling","args":[{"type":"double"}],"returns":"real"}
|
||||
{"name":"ceiling","args":[{"type":"double precision"}],"returns":"real"}
|
||||
{"name":"ceiling","args":[{"type":"float"}],"returns":"real"}
|
||||
{"name":"changes","returns":"integer"}
|
||||
{"name":"char","args":[{"type":"integer","mode":"v"}],"returns":"text"}
|
||||
{"name":"coalesce","args":[{"type":"any"},{"type":"any"},{"type":"any","mode":"v"}],"returns":"any"}
|
||||
{"name":"concat","args":[{"type":"any"},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"concat_ws","args":[{"type":"text"},{"type":"any"},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"cos","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"cosh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"count","kind":"a","returns":"integer"}
|
||||
{"name":"count","kind":"a","args":[{"type":"any"}],"returns":"integer"}
|
||||
{"name":"cume_dist","kind":"w","returns":"real"}
|
||||
{"name":"current_date","returns":"text"}
|
||||
{"name":"current_time","returns":"text"}
|
||||
{"name":"current_timestamp","returns":"text"}
|
||||
{"name":"date","args":[{"type":"any","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"datetime","args":[{"type":"any","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"degrees","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"dense_rank","kind":"w","returns":"integer"}
|
||||
{"name":"exp","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"first_value","kind":"w","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"floor","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"floor","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"int"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"tinyint"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"smallint"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"mediumint"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"bigint"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"unsigned big int"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"int2"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"int8"}],"returns":"integer"}
|
||||
{"name":"floor","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"floor","args":[{"type":"double"}],"returns":"real"}
|
||||
{"name":"floor","args":[{"type":"double precision"}],"returns":"real"}
|
||||
{"name":"floor","args":[{"type":"float"}],"returns":"real"}
|
||||
{"name":"format","args":[{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"glob","args":[{"type":"text"},{"type":"text"}],"returns":"integer"}
|
||||
{"name":"group_concat","kind":"a","args":[{"type":"text"}],"returns":"text","nullable":true}
|
||||
{"name":"group_concat","kind":"a","args":[{"type":"text"},{"type":"text"}],"returns":"text","nullable":true}
|
||||
{"name":"hex","args":[{"type":"blob"}],"returns":"text"}
|
||||
{"name":"if","args":[{"type":"any"},{"type":"any"},{"type":"any","mode":"v"}],"returns":"any"}
|
||||
{"name":"ifnull","args":[{"type":"any"},{"type":"any"}],"returns":"any"}
|
||||
{"name":"iif","args":[{"type":"any"},{"type":"any"},{"type":"any","mode":"v"}],"returns":"any"}
|
||||
{"name":"instr","args":[{"type":"any"},{"type":"any"}],"returns":"integer"}
|
||||
{"name":"json","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_array","args":[{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_array_insert","args":[{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_array_length","args":[{"type":"any"}],"returns":"integer","nullable":true}
|
||||
{"name":"json_array_length","args":[{"type":"any"},{"type":"text"}],"returns":"integer","nullable":true}
|
||||
{"name":"json_error_position","args":[{"type":"text"}],"returns":"integer"}
|
||||
{"name":"json_extract","args":[{"type":"text","mode":"v"}],"returns":"any","nullable":true}
|
||||
{"name":"json_group_array","kind":"a","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_group_object","kind":"a","args":[{"type":"text"},{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_insert","args":[{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_object","args":[{"type":"text","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_patch","args":[{"type":"any"},{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_pretty","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_pretty","args":[{"type":"any"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"json_quote","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"json_remove","args":[{"type":"text","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_replace","args":[{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_set","args":[{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"json_type","args":[{"type":"any"}],"returns":"text","nullable":true}
|
||||
{"name":"json_type","args":[{"type":"any"},{"type":"text"}],"returns":"text","nullable":true}
|
||||
{"name":"json_valid","args":[{"type":"any"}],"returns":"integer"}
|
||||
{"name":"json_valid","args":[{"type":"any"},{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"jsonb","args":[{"type":"any"}],"returns":"blob"}
|
||||
{"name":"jsonb_array","args":[{"type":"any","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_array_insert","args":[{"type":"any","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_extract","args":[{"type":"text","mode":"v"}],"returns":"any","nullable":true}
|
||||
{"name":"jsonb_group_array","kind":"a","args":[{"type":"any"}],"returns":"blob"}
|
||||
{"name":"jsonb_group_object","kind":"a","args":[{"type":"text"},{"type":"any"}],"returns":"blob"}
|
||||
{"name":"jsonb_insert","args":[{"type":"any","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_object","args":[{"type":"text","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_patch","args":[{"type":"any"},{"type":"any"}],"returns":"blob"}
|
||||
{"name":"jsonb_remove","args":[{"type":"text","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_replace","args":[{"type":"any","mode":"v"}],"returns":"blob"}
|
||||
{"name":"jsonb_set","args":[{"type":"any","mode":"v"}],"returns":"blob"}
|
||||
{"name":"julianday","args":[{"type":"any","mode":"v"}],"returns":"real","nullable":true}
|
||||
{"name":"lag","kind":"w","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"lag","kind":"w","args":[{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"lag","kind":"w","args":[{"type":"any"},{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"last_insert_rowid","returns":"integer"}
|
||||
{"name":"last_value","kind":"w","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"lead","kind":"w","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"lead","kind":"w","args":[{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"lead","kind":"w","args":[{"type":"any"},{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"length","args":[{"type":"text"}],"returns":"integer"}
|
||||
{"name":"like","args":[{"type":"text"},{"type":"text"}],"returns":"integer"}
|
||||
{"name":"like","args":[{"type":"text"},{"type":"text"},{"type":"text"}],"returns":"integer"}
|
||||
{"name":"likelihood","args":[{"type":"any"},{"type":"any"}],"returns":"any"}
|
||||
{"name":"likely","args":[{"type":"any"}],"returns":"any"}
|
||||
{"name":"ln","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"log","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"log","args":[{"type":"real"},{"type":"real"}],"returns":"real"}
|
||||
{"name":"log10","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"log2","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"lower","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"ltrim","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"ltrim","args":[{"type":"text"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"max","kind":"a","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"max","args":[{"type":"any"},{"type":"any","mode":"v"}],"returns":"any"}
|
||||
{"name":"min","kind":"a","args":[{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"min","args":[{"type":"any"},{"type":"any","mode":"v"}],"returns":"any"}
|
||||
{"name":"mod","args":[{"type":"real"},{"type":"real"}],"returns":"real"}
|
||||
{"name":"nth_value","kind":"w","args":[{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"ntile","kind":"w","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"nullif","args":[{"type":"any"},{"type":"any"}],"returns":"any","nullable":true}
|
||||
{"name":"octet_length","args":[{"type":"any"}],"returns":"integer"}
|
||||
{"name":"percent_rank","kind":"w","returns":"real"}
|
||||
{"name":"pi","returns":"real"}
|
||||
{"name":"pow","args":[{"type":"real"},{"type":"real"}],"returns":"real"}
|
||||
{"name":"power","args":[{"type":"real"},{"type":"real"}],"returns":"real"}
|
||||
{"name":"printf","args":[{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text"}
|
||||
{"name":"quote","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"radians","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"random","returns":"integer"}
|
||||
{"name":"randomblob","args":[{"type":"integer"}],"returns":"blob"}
|
||||
{"name":"rank","kind":"w","returns":"integer"}
|
||||
{"name":"replace","args":[{"type":"text"},{"type":"text"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"round","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"round","args":[{"type":"real"},{"type":"integer"}],"returns":"real"}
|
||||
{"name":"row_number","kind":"w","returns":"integer"}
|
||||
{"name":"rtrim","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"rtrim","args":[{"type":"text"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"sign","args":[{"type":"real"}],"returns":"integer","nullable":true}
|
||||
{"name":"sin","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"sinh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"sqlite_compileoption_get","args":[{"type":"integer"}],"returns":"text","nullable":true}
|
||||
{"name":"sqlite_compileoption_used","args":[{"type":"text"}],"returns":"integer"}
|
||||
{"name":"sqlite_source_id","returns":"text"}
|
||||
{"name":"sqlite_version","returns":"text"}
|
||||
{"name":"sqrt","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"strftime","args":[{"type":"text","has_default":true},{"type":"any","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"string_agg","kind":"a","args":[{"type":"text"},{"type":"text"}],"returns":"text","nullable":true}
|
||||
{"name":"substr","args":[{"type":"any"},{"type":"integer"}],"returns":"text"}
|
||||
{"name":"substr","args":[{"type":"any"},{"type":"integer"},{"type":"integer"}],"returns":"text"}
|
||||
{"name":"substring","args":[{"type":"any"},{"type":"integer"}],"returns":"text"}
|
||||
{"name":"substring","args":[{"type":"any"},{"type":"integer"},{"type":"integer"}],"returns":"text"}
|
||||
{"name":"subtype","args":[{"type":"any"}],"returns":"integer"}
|
||||
{"name":"sum","kind":"a","args":[{"type":"any"}],"returns":"real","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"integer"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"int"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"tinyint"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"smallint"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"mediumint"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"bigint"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"unsigned big int"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"int2"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"int8"}],"returns":"integer","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"real"}],"returns":"real","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"double"}],"returns":"real","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"double precision"}],"returns":"real","nullable":true}
|
||||
{"name":"sum","kind":"a","args":[{"type":"float"}],"returns":"real","nullable":true}
|
||||
{"name":"tan","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"tanh","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"time","args":[{"type":"any","mode":"v"}],"returns":"text","nullable":true}
|
||||
{"name":"timediff","args":[{"type":"any"},{"type":"any"}],"returns":"text","nullable":true}
|
||||
{"name":"total","kind":"a","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"total_changes","returns":"integer"}
|
||||
{"name":"trim","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"trim","args":[{"type":"text"},{"type":"text"}],"returns":"text"}
|
||||
{"name":"trunc","args":[{"type":"any"}],"returns":"real"}
|
||||
{"name":"trunc","args":[{"type":"integer"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"int"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"tinyint"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"smallint"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"mediumint"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"bigint"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"unsigned big int"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"int2"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"int8"}],"returns":"integer"}
|
||||
{"name":"trunc","args":[{"type":"real"}],"returns":"real"}
|
||||
{"name":"trunc","args":[{"type":"double"}],"returns":"real"}
|
||||
{"name":"trunc","args":[{"type":"double precision"}],"returns":"real"}
|
||||
{"name":"trunc","args":[{"type":"float"}],"returns":"real"}
|
||||
{"name":"typeof","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"unhex","args":[{"type":"text"}],"returns":"blob","nullable":true}
|
||||
{"name":"unhex","args":[{"type":"text"},{"type":"text"}],"returns":"blob","nullable":true}
|
||||
{"name":"unicode","args":[{"type":"text"}],"returns":"integer"}
|
||||
{"name":"unistr","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"unistr_quote","args":[{"type":"any"}],"returns":"text"}
|
||||
{"name":"unixepoch","args":[{"type":"any","mode":"v"}],"returns":"real","nullable":true}
|
||||
{"name":"unlikely","args":[{"type":"any"}],"returns":"any"}
|
||||
{"name":"upper","args":[{"type":"text"}],"returns":"text"}
|
||||
{"name":"zeroblob","args":[{"type":"integer"}],"returns":"blob"}
|
||||
1
internal/engine/sqlite/dialect/operators.jsonl
Normal file
1
internal/engine/sqlite/dialect/operators.jsonl
Normal file
@@ -0,0 +1 @@
|
||||
{"name": "||", "left": "text", "right": "text", "result": "text"}
|
||||
10
internal/engine/sqlite/dialect/types.jsonl
Normal file
10
internal/engine/sqlite/dialect/types.jsonl
Normal file
@@ -0,0 +1,10 @@
|
||||
{"name": "integer", "category": "N", "aliases": ["int", "tinyint", "smallint", "mediumint", "bigint", "unsigned big int", "int2", "int8"]}
|
||||
{"name": "real", "category": "N", "aliases": ["double", "double precision", "float"]}
|
||||
{"name": "numeric", "category": "N", "aliases": ["decimal"]}
|
||||
{"name": "boolean", "category": "B", "aliases": ["bool"]}
|
||||
{"name": "text", "category": "S", "aliases": ["character", "varchar", "varying character", "nchar", "native character", "nvarchar", "clob"]}
|
||||
{"name": "blob", "category": "U"}
|
||||
{"name": "any", "category": "U"}
|
||||
{"name": "date", "category": "D"}
|
||||
{"name": "datetime", "category": "D"}
|
||||
{"name": "timestamp", "category": "D"}
|
||||
26
internal/engine/sqlite/extension.go
Normal file
26
internal/engine/sqlite/extension.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/sqlc-dev/sqlc/internal/core/seed"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
|
||||
)
|
||||
|
||||
// loadExtension returns the functions a compile option adds, read from the
|
||||
// option's directory under the dialect's extensions/. The name is either the
|
||||
// directory's — enable_fts5 — or a virtual table module the dialect maps to
|
||||
// one, since a schema that says CREATE VIRTUAL TABLE ... USING fts5 has said
|
||||
// which build of SQLite it runs on. An option the dialect has no data for
|
||||
// adds nothing.
|
||||
func loadExtension(name string) *catalog.Schema {
|
||||
dir, ok := seed.ExtensionDir(dialectFS, "dialect", name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
funcs, err := seed.Functions(dialectFS, dir)
|
||||
if err != nil {
|
||||
// The list is embedded in the binary: a failure here means sqlc was
|
||||
// built from a broken tree, which no caller can do anything about.
|
||||
panic(err)
|
||||
}
|
||||
return &catalog.Schema{Name: "main", Funcs: funcs}
|
||||
}
|
||||
59
internal/engine/sqlite/format.go
Normal file
59
internal/engine/sqlite/format.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// QuoteIdent quotes an identifier when printing it bare would change what it
|
||||
// names: the parser folds unquoted identifiers to lower case, so any name
|
||||
// that is not already a plain lower-case identifier — or that collides with
|
||||
// a keyword — only survives a parse round-trip inside double quotes.
|
||||
func (p *Parser) QuoteIdent(s string) string {
|
||||
if s == "" || p.IsReservedKeyword(s) || !plainIdent(s) {
|
||||
return `"` + strings.ReplaceAll(s, `"`, `""`) + `"`
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// plainIdent reports whether s parses back as itself unquoted: ASCII
|
||||
// lower-case letters, digits and underscores, not starting with a digit.
|
||||
func plainIdent(s string) bool {
|
||||
for i, r := range s {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z', r == '_':
|
||||
case r >= '0' && r <= '9':
|
||||
if i == 0 {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TypeName returns the SQL type name for the given namespace and name.
|
||||
func (p *Parser) TypeName(ns, name string) string {
|
||||
if ns != "" {
|
||||
return ns + "." + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Param returns the parameter placeholder for the given number. SQLite
|
||||
// takes both a bare ? (the next index) and an explicit ?N, and they are
|
||||
// not interchangeable — reordered ?N parameters bind by their numbers —
|
||||
// so the one the author wrote is the one printed.
|
||||
func (p *Parser) Param(n int, numbered bool) string {
|
||||
if numbered {
|
||||
return "?" + strconv.Itoa(n)
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
// Cast returns a type cast expression.
|
||||
// SQLite uses CAST(expr AS type) syntax.
|
||||
func (p *Parser) Cast(arg, typeName string) string {
|
||||
return "CAST(" + arg + " AS " + typeName + ")"
|
||||
}
|
||||
158
internal/engine/sqlite/parse.go
Normal file
158
internal/engine/sqlite/parse.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
meyer "github.com/sqlc-dev/meyer/ast"
|
||||
"github.com/sqlc-dev/meyer/parser"
|
||||
mtoken "github.com/sqlc-dev/meyer/token"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/source"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/sqlerr"
|
||||
)
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{}
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
}
|
||||
|
||||
// parseOptions accepts ORDER BY and LIMIT on UPDATE and DELETE. SQLite has
|
||||
// them only when built with SQLITE_ENABLE_UPDATE_DELETE_LIMIT, which meyer
|
||||
// cannot tell from the SQL, so the caller has to ask. sqlc has always
|
||||
// accepted them.
|
||||
var parseOptions = parser.Options{UpdateDeleteLimit: true}
|
||||
|
||||
func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
|
||||
f, err := p.ParseFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The compiler skips statements sqlc has no node for (PRAGMA and
|
||||
// friends); the formatter must see them, so the filter lives here,
|
||||
// not in ParseFile.
|
||||
var stmts []ast.Statement
|
||||
for _, stmt := range f.Stmts {
|
||||
if _, ok := stmt.Raw.Stmt.(*ast.TODO); ok {
|
||||
continue
|
||||
}
|
||||
stmts = append(stmts, stmt)
|
||||
}
|
||||
return stmts, nil
|
||||
}
|
||||
|
||||
// ParseFile parses like Parse and also carries the file's comments, taken
|
||||
// from the trivia meyer's single lexer pass produces alongside the tokens.
|
||||
func (p *Parser) ParseFile(r io.Reader) (*ast.File, error) {
|
||||
blob, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src := string(blob)
|
||||
parsed, err := parseOptions.ParseFile(src)
|
||||
if err != nil {
|
||||
return nil, normalizeErr(err)
|
||||
}
|
||||
|
||||
var stmts []ast.Statement
|
||||
// loc tracks the first byte after the previous statement's terminator, so
|
||||
// that a statement's extent covers the comments written above it. sqlc
|
||||
// reads the "-- name:" annotation out of that range.
|
||||
loc := 0
|
||||
for _, raw := range parsed.Stmts {
|
||||
converter := &cc{src: src}
|
||||
// A statement sqlc has no node for converts to a TODO and stays in
|
||||
// the list: the formatter needs its extent to keep it as written,
|
||||
// and Parse filters it out for the compiler.
|
||||
stmts = append(stmts, ast.Statement{
|
||||
Raw: &ast.RawStmt{
|
||||
Stmt: converter.convert(raw),
|
||||
StmtLocation: loc,
|
||||
StmtLen: trimTerminator(src, raw) - loc,
|
||||
},
|
||||
})
|
||||
loc = raw.End()
|
||||
}
|
||||
|
||||
var comments []ast.Comment
|
||||
for _, tr := range parsed.Trivia {
|
||||
if tr.Kind != mtoken.COMMENT {
|
||||
continue
|
||||
}
|
||||
comments = append(comments, ast.Comment{
|
||||
Text: strings.TrimRight(tr.Text(src), " \t\r\n"),
|
||||
Start: tr.Pos,
|
||||
End: tr.End,
|
||||
OwnLine: ownLine(src, tr.Pos),
|
||||
})
|
||||
}
|
||||
return &ast.File{Stmts: stmts, Comments: comments}, nil
|
||||
}
|
||||
|
||||
// ownLine reports that only blank space sits between the preceding line
|
||||
// break and pos.
|
||||
func ownLine(src string, pos int) bool {
|
||||
for j := pos - 1; j >= 0; j-- {
|
||||
switch src[j] {
|
||||
case '\n':
|
||||
return true
|
||||
case ' ', '\t', '\r':
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// trimTerminator returns the end of stmt with its terminating semicolon, and
|
||||
// any space before it, removed. A statement's span runs through the
|
||||
// semicolon, but sqlc's statement text does not include it.
|
||||
func trimTerminator(src string, stmt meyer.Stmt) int {
|
||||
end := stmt.End()
|
||||
if end > stmt.Pos() && end <= len(src) && src[end-1] == ';' {
|
||||
end--
|
||||
}
|
||||
for end > stmt.Pos() && isSpace(src[end-1]) {
|
||||
end--
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
func isSpace(b byte) bool {
|
||||
switch b {
|
||||
case ' ', '\t', '\n', '\r', '\f':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// normalizeErr turns a meyer parse failure into the positioned error sqlc
|
||||
// reports to the user. meyer's message and byte offset match SQLite's own.
|
||||
func normalizeErr(err error) error {
|
||||
var perr *parser.Error
|
||||
if !errors.As(err, &perr) {
|
||||
return err
|
||||
}
|
||||
line, column := parser.LineCol(perr.SQL, perr.Offset)
|
||||
if line == 0 {
|
||||
return errors.New(perr.Message)
|
||||
}
|
||||
return &sqlerr.Error{
|
||||
Message: perr.Message,
|
||||
Line: line,
|
||||
Column: column,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) CommentSyntax() source.CommentSyntax {
|
||||
return source.CommentSyntax{
|
||||
Dash: true,
|
||||
Hash: false,
|
||||
SlashStar: true,
|
||||
}
|
||||
}
|
||||
10
internal/engine/sqlite/reserved.go
Normal file
10
internal/engine/sqlite/reserved.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package sqlite
|
||||
|
||||
import "github.com/sqlc-dev/meyer/token"
|
||||
|
||||
// IsReservedKeyword reports whether s is a keyword in SQLite; see
|
||||
// https://sqlite.org/lang_keywords.html. The table lives in meyer, which
|
||||
// transcribes it from SQLite's own aKeywordTable.
|
||||
func (p *Parser) IsReservedKeyword(s string) bool {
|
||||
return token.Lookup(s) != token.ID
|
||||
}
|
||||
34
internal/engine/sqlite/seed.go
Normal file
34
internal/engine/sqlite/seed.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"sync"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/core"
|
||||
"github.com/sqlc-dev/sqlc/internal/core/seed"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
|
||||
)
|
||||
|
||||
// The dialect directory describes SQLite's type system, and functions.jsonl is
|
||||
// its standard library. Both the analysis core and the catalog the legacy
|
||||
// compiler builds read them.
|
||||
//
|
||||
//go:embed dialect
|
||||
var dialectFS embed.FS
|
||||
|
||||
// Dialect returns the catalog option that seeds SQLite's type system.
|
||||
func Dialect() core.Option {
|
||||
return seed.Dialect(dialectFS, "dialect")
|
||||
}
|
||||
|
||||
// stdlib is SQLite's functions in the form the catalog uses. They are embedded
|
||||
// in the binary and never change within a run, so they are read once.
|
||||
var stdlib = sync.OnceValue(func() []*catalog.Function {
|
||||
funcs, err := seed.Functions(dialectFS, "dialect")
|
||||
if err != nil {
|
||||
// A failure here means sqlc was built from a broken tree, which no
|
||||
// caller can do anything about.
|
||||
panic(err)
|
||||
}
|
||||
return funcs
|
||||
})
|
||||
13
internal/engine/sqlite/stdlib.go
Normal file
13
internal/engine/sqlite/stdlib.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
|
||||
)
|
||||
|
||||
// defaultSchema is SQLite's standard library, read from the dialect
|
||||
// directory's functions.jsonl, which internal/goldeneye generates from a
|
||||
// default build of SQLite. The functions further compile options add live
|
||||
// under the dialect's extensions/, one directory per option.
|
||||
func defaultSchema(name string) *catalog.Schema {
|
||||
return &catalog.Schema{Name: name, Funcs: stdlib()}
|
||||
}
|
||||
144
internal/engine/sqlite/utils.go
Normal file
144
internal/engine/sqlite/utils.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
meyer "github.com/sqlc-dev/meyer/ast"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
||||
)
|
||||
|
||||
// identifier folds a name the way SQLite resolves one: an unquoted name is
|
||||
// case-insensitive and normalizes to lower case, while a quoted name keeps
|
||||
// the spelling that appeared between the quotes.
|
||||
func identifier(n *meyer.Ident) string {
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
if n.Quote == meyer.QuoteNone {
|
||||
return strings.ToLower(n.Name)
|
||||
}
|
||||
return n.Name
|
||||
}
|
||||
|
||||
// NewIdentifier returns the folded name of id as an ast.String, the shape
|
||||
// sqlc uses for the parts of a ColumnRef or a function name.
|
||||
func NewIdentifier(id *meyer.Ident) *ast.String {
|
||||
return &ast.String{Str: identifier(id)}
|
||||
}
|
||||
|
||||
func parseTableName(n *meyer.QualifiedName) *ast.TableName {
|
||||
if n == nil {
|
||||
return &ast.TableName{}
|
||||
}
|
||||
return &ast.TableName{
|
||||
Schema: identifier(n.Schema),
|
||||
Name: identifier(n.Name),
|
||||
}
|
||||
}
|
||||
|
||||
// parseRangeVar builds the relation reference sqlc expects for a table named
|
||||
// in a FROM, INSERT, UPDATE or DELETE clause.
|
||||
func parseRangeVar(n *meyer.QualifiedName, alias *meyer.Ident) *ast.RangeVar {
|
||||
rv := &ast.RangeVar{}
|
||||
if n == nil {
|
||||
return rv
|
||||
}
|
||||
rv.Location = n.Pos()
|
||||
name := identifier(n.Name)
|
||||
rv.Relname = &name
|
||||
if n.Schema != nil {
|
||||
schema := identifier(n.Schema)
|
||||
rv.Schemaname = &schema
|
||||
}
|
||||
if alias != nil {
|
||||
aliasName := identifier(alias)
|
||||
rv.Alias = &ast.Alias{Aliasname: &aliasName}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
// typeName renders a type the way sqlc's type resolution expects to see it:
|
||||
// the tokens joined without separators, matching what the ANTLR parser's
|
||||
// GetText produced. "UNSIGNED BIG INT" becomes "UNSIGNEDBIGINT" and
|
||||
// "DECIMAL(10, 5)" becomes "DECIMAL(10,5)".
|
||||
func typeName(n *meyer.TypeName) string {
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString(strings.ReplaceAll(n.Name, " ", ""))
|
||||
if len(n.Args) > 0 {
|
||||
sb.WriteString("(")
|
||||
sb.WriteString(strings.Join(n.Args, ","))
|
||||
sb.WriteString(")")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// typeSpelling returns the type as the author wrote it — identifier tokens
|
||||
// space-joined, arguments as written — for the formatter to print back.
|
||||
// typeName folds the spaces out for catalog matching.
|
||||
func typeSpelling(n *meyer.TypeName) string {
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString(n.Name)
|
||||
if len(n.Args) > 0 {
|
||||
sb.WriteString("(")
|
||||
sb.WriteString(strings.Join(n.Args, ","))
|
||||
sb.WriteString(")")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// representableColumn reports whether a column definition round-trips
|
||||
// through the fields ast.ColumnDef models: a plain NOT NULL, a plain
|
||||
// PRIMARY KEY, or nothing. Anything else — a named constraint, a conflict
|
||||
// clause, ASC/DESC or AUTOINCREMENT, DEFAULT, UNIQUE, CHECK, COLLATE,
|
||||
// REFERENCES, GENERATED — is parsed away, so a statement printing such a
|
||||
// column would lose it. NOT NULL next to PRIMARY KEY is also beyond the
|
||||
// node: SQLite's PRIMARY KEY does not imply NOT NULL, and the printed form
|
||||
// carries only one of the pair.
|
||||
func representableColumn(def *meyer.ColumnDef) bool {
|
||||
var notNull, primary bool
|
||||
for _, con := range def.Constraints {
|
||||
switch {
|
||||
case con.Name != nil:
|
||||
return false
|
||||
case con.Kind == meyer.ColumnNotNull && con.OnConflict == meyer.ConflictDefault:
|
||||
notNull = true
|
||||
case con.Kind == meyer.ColumnPrimaryKey && con.OnConflict == meyer.ConflictDefault &&
|
||||
con.Order == meyer.SortDefault && !con.AutoIncrement:
|
||||
primary = true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return !(notNull && primary)
|
||||
}
|
||||
|
||||
// hasPrimaryKeyConstraint reports whether a column definition carries a
|
||||
// PRIMARY KEY constraint.
|
||||
func hasPrimaryKeyConstraint(constraints []*meyer.ColumnConstraint) bool {
|
||||
for _, constraint := range constraints {
|
||||
if constraint.Kind == meyer.ColumnPrimaryKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasNotNullConstraint reports whether a column definition guarantees a
|
||||
// value. A PRIMARY KEY implies NOT NULL for the purposes of code
|
||||
// generation, matching the behavior of the previous parser.
|
||||
func hasNotNullConstraint(constraints []*meyer.ColumnConstraint) bool {
|
||||
for _, constraint := range constraints {
|
||||
switch constraint.Kind {
|
||||
case meyer.ColumnNotNull, meyer.ColumnPrimaryKey:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user