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:
134
internal/core/seed/extension.go
Normal file
134
internal/core/seed/extension.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package seed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/core"
|
||||
)
|
||||
|
||||
// applyExtension applies the extension directory dir, relative to the
|
||||
// dialect, to a catalog that has already been seeded. Unlike the dialect's
|
||||
// own seed, an extension lands in a catalog full of types, so everything it
|
||||
// names is resolved against what is there before being created.
|
||||
func applyExtension(cat *core.Catalog, fsys fs.FS, dir string) error {
|
||||
name := path.Base(dir)
|
||||
sub, err := fs.Sub(fsys, dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("seed: extension %s: %w", name, err)
|
||||
}
|
||||
e := &extension{cat: cat}
|
||||
if err := stream(sub, TypesFile, e.addType); err != nil {
|
||||
return fmt.Errorf("extension %s: %w", name, err)
|
||||
}
|
||||
if err := stream(sub, FunctionsFile, e.addFunction); err != nil {
|
||||
return fmt.Errorf("extension %s: %w", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type extension struct {
|
||||
cat *core.Catalog
|
||||
}
|
||||
|
||||
// addType registers a type the extension defines. The type joins the
|
||||
// dialect's rules the way a type declared by a schema does: it gains the
|
||||
// comparison operators, and implicit casts to the types of its category when
|
||||
// the dialect's seed declared that category mutually castable.
|
||||
func (e *extension) addType(t Type) error {
|
||||
for _, name := range append([]string{t.Name}, t.Aliases...) {
|
||||
if _, err := e.cat.TypeOID(name); err == nil {
|
||||
// The dialect, or an earlier extension, already ships this type.
|
||||
continue
|
||||
}
|
||||
oid, err := e.cat.CreateUserType(name, t.Category)
|
||||
if err != nil {
|
||||
return fmt.Errorf("type %q: %w", name, err)
|
||||
}
|
||||
if err := e.categoryCasts(oid, t.Category); err != nil {
|
||||
return fmt.Errorf("type %q: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// categoryCasts makes a new type implicitly castable to and from the types
|
||||
// already in its category, under the same rule the dialect's seed applied to
|
||||
// the types it was born with.
|
||||
func (e *extension) categoryCasts(oid int64, category string) error {
|
||||
categories, err := e.cat.DialectFlag(e.cat.SeededDialectOID(), core.FlagCastCategories)
|
||||
if err != nil || categories == "" {
|
||||
return err
|
||||
}
|
||||
if categories != "*" && !strings.Contains(categories, category) {
|
||||
return nil
|
||||
}
|
||||
peers, err := e.cat.TypeOIDsInCategory(category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, peer := range peers {
|
||||
if peer == oid {
|
||||
continue
|
||||
}
|
||||
for _, cast := range [][2]int64{{oid, peer}, {peer, oid}} {
|
||||
if err := e.cat.CreateCast(core.CastSpec{
|
||||
SourceTypeOID: cast[0],
|
||||
TargetTypeOID: cast[1],
|
||||
Context: "i",
|
||||
DialectOID: e.cat.SeededDialectOID(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *extension) addFunction(fn Function) error {
|
||||
returnOID, err := e.funcType(fn.Returns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("function %q: %w", fn.Name, err)
|
||||
}
|
||||
args := make([]core.ProcArg, 0, len(fn.Args))
|
||||
for _, a := range fn.Args {
|
||||
// An out or table parameter is part of the result, not the call.
|
||||
switch a.Mode {
|
||||
case "o", "t":
|
||||
continue
|
||||
}
|
||||
argOID, err := e.funcType(a.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("function %q: %w", fn.Name, err)
|
||||
}
|
||||
args = append(args, core.ProcArg{
|
||||
Name: a.Name,
|
||||
TypeOID: argOID,
|
||||
Mode: a.Mode,
|
||||
HasDefault: a.HasDefault,
|
||||
})
|
||||
}
|
||||
_, err = e.cat.CreateProc(core.ProcSpec{
|
||||
Name: fn.Name,
|
||||
DialectOID: e.cat.SeededDialectOID(),
|
||||
Kind: fn.Kind,
|
||||
ReturnTypeOID: returnOID,
|
||||
ReturnNullable: fn.Nullable,
|
||||
Args: args,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("function %q: %w", fn.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// funcType resolves a type a function signature names, registering the ones
|
||||
// no seed bothered to list the way the dialect's own function list does.
|
||||
func (e *extension) funcType(name string) (int64, error) {
|
||||
if name == "" {
|
||||
return 0, nil
|
||||
}
|
||||
return e.cat.ResolvePseudoTypeExpr(core.ParseTypeExpr(name))
|
||||
}
|
||||
Reference in New Issue
Block a user