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

This commit is contained in:
upstream-mirror
2026-09-16 02:17:34 +05:00
commit 0f4df23290
7340 changed files with 238743 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
package opts
import "fmt"
type SQLDriver string
const (
SQLPackagePGXV4 string = "pgx/v4"
SQLPackagePGXV5 string = "pgx/v5"
SQLPackageStandard string = "database/sql"
)
var validPackages = map[string]struct{}{
string(SQLPackagePGXV4): {},
string(SQLPackagePGXV5): {},
string(SQLPackageStandard): {},
}
func validatePackage(sqlPackage string) error {
if _, found := validPackages[sqlPackage]; !found {
return fmt.Errorf("unknown SQL package: %s", sqlPackage)
}
return nil
}
const (
SQLDriverPGXV4 SQLDriver = "github.com/jackc/pgx/v4"
SQLDriverPGXV5 = "github.com/jackc/pgx/v5"
SQLDriverLibPQ = "github.com/lib/pq"
SQLDriverGoSQLDriverMySQL = "github.com/go-sql-driver/mysql"
)
var validDrivers = map[string]struct{}{
string(SQLDriverPGXV4): {},
string(SQLDriverPGXV5): {},
string(SQLDriverLibPQ): {},
string(SQLDriverGoSQLDriverMySQL): {},
}
func validateDriver(sqlDriver string) error {
if _, found := validDrivers[sqlDriver]; !found {
return fmt.Errorf("unknown SQL driver: %s", sqlDriver)
}
return nil
}
func (d SQLDriver) IsPGX() bool {
return d == SQLDriverPGXV4 || d == SQLDriverPGXV5
}
func (d SQLDriver) IsGoSQLDriverMySQL() bool {
return d == SQLDriverGoSQLDriverMySQL
}
func (d SQLDriver) Package() string {
switch d {
case SQLDriverPGXV4:
return SQLPackagePGXV4
case SQLDriverPGXV5:
return SQLPackagePGXV5
default:
return SQLPackageStandard
}
}

View File

@@ -0,0 +1,192 @@
package opts
import (
"encoding/json"
"fmt"
"go/types"
"regexp"
"strings"
"github.com/fatih/structtag"
)
type GoType struct {
Path string `json:"import" yaml:"import"`
Package string `json:"package" yaml:"package"`
Name string `json:"type" yaml:"type"`
Pointer bool `json:"pointer" yaml:"pointer"`
Slice bool `json:"slice" yaml:"slice"`
Spec string `json:"-"`
BuiltIn bool `json:"-"`
}
type ParsedGoType struct {
ImportPath string
Package string
TypeName string
BasicType bool
StructTag string
}
func (o *GoType) MarshalJSON() ([]byte, error) {
if o.Spec != "" {
return json.Marshal(o.Spec)
}
type alias GoType
return json.Marshal(alias(*o))
}
func (o *GoType) UnmarshalJSON(data []byte) error {
var spec string
if err := json.Unmarshal(data, &spec); err == nil {
*o = GoType{Spec: spec}
return nil
}
type alias GoType
var a alias
if err := json.Unmarshal(data, &a); err != nil {
return err
}
*o = GoType(a)
return nil
}
func (o *GoType) UnmarshalYAML(unmarshal func(any) error) error {
var spec string
if err := unmarshal(&spec); err == nil {
*o = GoType{Spec: spec}
return nil
}
type alias GoType
var a alias
if err := unmarshal(&a); err != nil {
return err
}
*o = GoType(a)
return nil
}
var validIdentifier = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
var versionNumber = regexp.MustCompile(`^v[0-9]+$`)
var invalidIdentifier = regexp.MustCompile(`[^a-zA-Z0-9_]`)
func generatePackageID(importPath string) (string, bool) {
parts := strings.Split(importPath, "/")
name := parts[len(parts)-1]
// If the last part of the import path is a valid identifier, assume that's the package name
if versionNumber.MatchString(name) && len(parts) >= 2 {
name = parts[len(parts)-2]
return invalidIdentifier.ReplaceAllString(strings.ToLower(name), "_"), true
}
if validIdentifier.MatchString(name) {
return name, false
}
return invalidIdentifier.ReplaceAllString(strings.ToLower(name), "_"), true
}
// validate GoType
func (gt GoType) parse() (*ParsedGoType, error) {
var o ParsedGoType
if gt.Spec == "" {
// TODO: Validation
if gt.Path == "" && gt.Package != "" {
return nil, fmt.Errorf("Package override `go_type`: package name requires an import path")
}
var pkg string
var pkgNeedsAlias bool
if gt.Package == "" && gt.Path != "" {
pkg, pkgNeedsAlias = generatePackageID(gt.Path)
if pkgNeedsAlias {
o.Package = pkg
}
} else {
pkg = gt.Package
o.Package = gt.Package
}
o.ImportPath = gt.Path
o.TypeName = gt.Name
o.BasicType = gt.Path == "" && gt.Package == ""
if pkg != "" {
o.TypeName = pkg + "." + o.TypeName
}
if gt.Pointer {
o.TypeName = "*" + o.TypeName
}
if gt.Slice {
o.TypeName = "[]" + o.TypeName
}
return &o, nil
}
input := gt.Spec
lastDot := strings.LastIndex(input, ".")
lastSlash := strings.LastIndex(input, "/")
typename := input
if lastDot == -1 && lastSlash == -1 {
// if the type name has no slash and no dot, validate that the type is a basic Go type
var found bool
for _, typ := range types.Typ {
info := typ.Info()
if info == 0 {
continue
}
if info&types.IsUntyped != 0 {
continue
}
if typename == typ.Name() {
found = true
}
}
if !found {
return nil, fmt.Errorf("Package override `go_type` specifier %q is not a Go basic type e.g. 'string'", input)
}
o.BasicType = true
} else {
// assume the type lives in a Go package
if lastDot == -1 {
return nil, fmt.Errorf("Package override `go_type` specifier %q is not the proper format, expected 'package.type', e.g. 'github.com/segmentio/ksuid.KSUID'", input)
}
typename = input[lastSlash+1:]
// a package name beginning with "go-" will give syntax errors in
// generated code. We should do the right thing and get the actual
// import name, but in lieu of that, stripping the leading "go-" may get
// us what we want.
typename = strings.TrimPrefix(typename, "go-")
typename = strings.TrimSuffix(typename, "-go")
o.ImportPath = input[:lastDot]
}
o.TypeName = typename
isPointer := input[0] == '*'
if isPointer {
o.ImportPath = o.ImportPath[1:]
o.TypeName = "*" + o.TypeName
}
return &o, nil
}
// GoStructTag is a raw Go struct tag.
type GoStructTag string
// Parse parses and validates a GoStructTag.
// The output is in a form convenient for codegen.
//
// Sample valid inputs/outputs:
//
// In Out
// empty string {}
// `a:"b"` {"a": "b"}
// `a:"b" x:"y,z"` {"a": "b", "x": "y,z"}
func (s GoStructTag) parse() (map[string]string, error) {
m := make(map[string]string)
tags, err := structtag.Parse(string(s))
if err != nil {
return nil, err
}
for _, tag := range tags.Tags() {
m[tag.Key] = tag.Value()
}
return m, nil
}

View File

@@ -0,0 +1,243 @@
package opts
import (
"encoding/json"
"fmt"
"maps"
"path/filepath"
"github.com/sqlc-dev/sqlc/internal/plugin"
)
type Options struct {
EmitInterface bool `json:"emit_interface" yaml:"emit_interface"`
EmitJsonTags bool `json:"emit_json_tags" yaml:"emit_json_tags"`
JsonTagsIdUppercase bool `json:"json_tags_id_uppercase" yaml:"json_tags_id_uppercase"`
EmitDbTags bool `json:"emit_db_tags" yaml:"emit_db_tags"`
EmitPreparedQueries bool `json:"emit_prepared_queries" yaml:"emit_prepared_queries"`
EmitExactTableNames bool `json:"emit_exact_table_names,omitempty" yaml:"emit_exact_table_names"`
EmitEmptySlices bool `json:"emit_empty_slices,omitempty" yaml:"emit_empty_slices"`
EmitExportedQueries bool `json:"emit_exported_queries" yaml:"emit_exported_queries"`
EmitResultStructPointers bool `json:"emit_result_struct_pointers" yaml:"emit_result_struct_pointers"`
EmitParamsStructPointers bool `json:"emit_params_struct_pointers" yaml:"emit_params_struct_pointers"`
EmitMethodsWithDbArgument bool `json:"emit_methods_with_db_argument,omitempty" yaml:"emit_methods_with_db_argument"`
EmitPointersForNullTypes bool `json:"emit_pointers_for_null_types" yaml:"emit_pointers_for_null_types"`
// nil inherits EmitPointersForNullTypes; non-nil overrides for enums only.
EmitPointersForNullEnumTypes *bool `json:"emit_pointers_for_null_enum_types,omitempty" yaml:"emit_pointers_for_null_enum_types"`
EmitEnumValidMethod bool `json:"emit_enum_valid_method,omitempty" yaml:"emit_enum_valid_method"`
EmitAllEnumValues bool `json:"emit_all_enum_values,omitempty" yaml:"emit_all_enum_values"`
EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"`
JsonTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"`
Package string `json:"package" yaml:"package"`
Out string `json:"out" yaml:"out"`
Overrides []Override `json:"overrides,omitempty" yaml:"overrides"`
Rename map[string]string `json:"rename,omitempty" yaml:"rename"`
SqlPackage string `json:"sql_package" yaml:"sql_package"`
SqlDriver string `json:"sql_driver" yaml:"sql_driver"`
OutputBatchFileName string `json:"output_batch_file_name,omitempty" yaml:"output_batch_file_name"`
OutputDbFileName string `json:"output_db_file_name,omitempty" yaml:"output_db_file_name"`
OutputModelsFileName string `json:"output_models_file_name,omitempty" yaml:"output_models_file_name"`
OutputModelsPath string `json:"output_models_path,omitempty" yaml:"output_models_path"`
OutputModelsPackage string `json:"output_models_package,omitempty" yaml:"output_models_package"`
OutputModelsImport string `json:"output_models_import,omitempty" yaml:"output_models_import"`
OutputModelsEmit *bool `json:"output_models_emit,omitempty" yaml:"output_models_emit"`
OutputQuerierFileName string `json:"output_querier_file_name,omitempty" yaml:"output_querier_file_name"`
OutputCopyfromFileName string `json:"output_copyfrom_file_name,omitempty" yaml:"output_copyfrom_file_name"`
OutputFilesSuffix string `json:"output_files_suffix,omitempty" yaml:"output_files_suffix"`
InflectionExcludeTableNames []string `json:"inflection_exclude_table_names,omitempty" yaml:"inflection_exclude_table_names"`
WrapErrors bool `json:"wrap_errors,omitempty" yaml:"wrap_errors"`
QueryParameterLimit *int32 `json:"query_parameter_limit,omitempty" yaml:"query_parameter_limit"`
OmitSqlcVersion bool `json:"omit_sqlc_version,omitempty" yaml:"omit_sqlc_version"`
OmitUnusedStructs bool `json:"omit_unused_structs,omitempty" yaml:"omit_unused_structs"`
BuildTags string `json:"build_tags,omitempty" yaml:"build_tags"`
Initialisms *[]string `json:"initialisms,omitempty" yaml:"initialisms"`
InitialismsMap map[string]struct{} `json:"-" yaml:"-"`
}
type GlobalOptions struct {
Overrides []Override `json:"overrides,omitempty" yaml:"overrides"`
Rename map[string]string `json:"rename,omitempty" yaml:"rename"`
}
func Parse(req *plugin.GenerateRequest) (*Options, error) {
options, err := parseOpts(req)
if err != nil {
return nil, err
}
global, err := parseGlobalOpts(req)
if err != nil {
return nil, err
}
if len(global.Overrides) > 0 {
options.Overrides = append(global.Overrides, options.Overrides...)
}
if len(global.Rename) > 0 {
if options.Rename == nil {
options.Rename = map[string]string{}
}
maps.Copy(options.Rename, global.Rename)
}
return options, nil
}
func parseOpts(req *plugin.GenerateRequest) (*Options, error) {
var options Options
if len(req.PluginOptions) == 0 {
return &options, nil
}
if err := json.Unmarshal(req.PluginOptions, &options); err != nil {
return nil, fmt.Errorf("unmarshalling plugin options: %w", err)
}
if options.Package == "" {
if options.Out != "" {
options.Package = filepath.Base(options.Out)
} else {
return nil, fmt.Errorf("invalid options: missing package name")
}
}
// Default the models package name to the base of the models path. When
// the user only configures output_models_emit: false (no path), fall
// back to the base of the import path.
if options.OutputModelsPackage == "" {
if options.OutputModelsPath != "" {
options.OutputModelsPackage = filepath.Base(options.OutputModelsPath)
} else if options.OutputModelsImport != "" {
options.OutputModelsPackage = filepath.Base(options.OutputModelsImport)
}
}
for i := range options.Overrides {
if err := options.Overrides[i].parse(req); err != nil {
return nil, err
}
}
if options.SqlPackage != "" {
if err := validatePackage(options.SqlPackage); err != nil {
return nil, fmt.Errorf("invalid options: %s", err)
}
}
if options.SqlDriver != "" {
if err := validateDriver(options.SqlDriver); err != nil {
return nil, fmt.Errorf("invalid options: %s", err)
}
}
if options.QueryParameterLimit == nil {
options.QueryParameterLimit = new(int32)
*options.QueryParameterLimit = 1
}
if options.Initialisms == nil {
options.Initialisms = new([]string)
*options.Initialisms = []string{"id"}
}
options.InitialismsMap = map[string]struct{}{}
for _, initial := range *options.Initialisms {
options.InitialismsMap[initial] = struct{}{}
}
return &options, nil
}
func parseGlobalOpts(req *plugin.GenerateRequest) (*GlobalOptions, error) {
var options GlobalOptions
if len(req.GlobalOptions) == 0 {
return &options, nil
}
if err := json.Unmarshal(req.GlobalOptions, &options); err != nil {
return nil, fmt.Errorf("unmarshalling global options: %w", err)
}
for i := range options.Overrides {
if err := options.Overrides[i].parse(req); err != nil {
return nil, err
}
}
return &options, nil
}
func ValidateOpts(opts *Options) error {
if opts.EmitMethodsWithDbArgument && opts.EmitPreparedQueries {
return fmt.Errorf("invalid options: emit_methods_with_db_argument and emit_prepared_queries options are mutually exclusive")
}
if *opts.QueryParameterLimit < 0 {
return fmt.Errorf("invalid options: query parameter limit must not be negative")
}
if err := validateModelsOptions(opts); err != nil {
return err
}
return nil
}
// ModelsEmitEnabled reports whether this codegen block should write the
// models file. Defaults to true when the option is unset.
func (o *Options) ModelsEmitEnabled() bool {
if o.OutputModelsEmit == nil {
return true
}
return *o.OutputModelsEmit
}
// ModelsImportAlias is the fixed Go import alias used for the models
// package in query files. Using a constant alias keeps the type qualifier
// consistent regardless of how the user names the actual package.
const ModelsImportAlias = "models"
// ModelsPackage returns the Go package name to use in the models file
// itself (i.e. the `package X` declaration). When the caller has not
// configured a separate models package, this is the same as Package.
func (o *Options) ModelsPackage() string {
if o.OutputModelsPackage != "" {
return o.OutputModelsPackage
}
return o.Package
}
// ModelsAreExternal reports whether model types live in a different Go
// package than the queries package. When true, query files must import the
// models package and reference types as `models.Type`.
func (o *Options) ModelsAreExternal() bool {
return o.OutputModelsImport != ""
}
// ModelsTypeQualifier returns the prefix to use when referencing a model
// type from a query file ("models."). Empty string when no qualifier is
// needed.
func (o *Options) ModelsTypeQualifier() string {
if o.ModelsAreExternal() {
return ModelsImportAlias + "."
}
return ""
}
func validateModelsOptions(opts *Options) error {
hasAnyModelsOpt := opts.OutputModelsPath != "" ||
opts.OutputModelsPackage != "" ||
opts.OutputModelsImport != "" ||
opts.OutputModelsEmit != nil
if !hasAnyModelsOpt {
return nil
}
if opts.OutputModelsImport == "" {
return fmt.Errorf("invalid options: output_models_import is required when any output_models_* option is set")
}
if opts.ModelsEmitEnabled() && opts.OutputModelsPath == "" {
return fmt.Errorf("invalid options: output_models_path is required when emitting models to a separate package")
}
if opts.ModelsEmitEnabled() && opts.OutputModelsPath == opts.Out {
return fmt.Errorf("invalid options: output_models_path matches out; models would overwrite the queries package")
}
return nil
}

View File

@@ -0,0 +1,176 @@
package opts
import (
"fmt"
"os"
"strings"
"github.com/sqlc-dev/sqlc/internal/codegen/sdk"
"github.com/sqlc-dev/sqlc/internal/pattern"
"github.com/sqlc-dev/sqlc/internal/plugin"
)
type Override struct {
// name of the golang type to use, e.g. `github.com/segmentio/ksuid.KSUID`
GoType GoType `json:"go_type" yaml:"go_type"`
// additional Go struct tags to add to this field, in raw Go struct tag form, e.g. `validate:"required" x:"y,z"`
// see https://github.com/sqlc-dev/sqlc/issues/534
GoStructTag GoStructTag `json:"go_struct_tag" yaml:"go_struct_tag"`
// fully qualified name of the Go type, e.g. `github.com/segmentio/ksuid.KSUID`
DBType string `json:"db_type" yaml:"db_type"`
Deprecated_PostgresType string `json:"postgres_type" yaml:"postgres_type"`
// for global overrides only when two different engines are in use
Engine string `json:"engine,omitempty" yaml:"engine"`
// True if the GoType should override if the matching type is nullable
Nullable bool `json:"nullable" yaml:"nullable"`
// True if the GoType should override if the matching type is unsiged.
Unsigned bool `json:"unsigned" yaml:"unsigned"`
// Deprecated. Use the `nullable` property instead
Deprecated_Null bool `json:"null" yaml:"null"`
// fully qualified name of the column, e.g. `accounts.id`
Column string `json:"column" yaml:"column"`
ColumnName *pattern.Match `json:"-"`
TableCatalog *pattern.Match `json:"-"`
TableSchema *pattern.Match `json:"-"`
TableRel *pattern.Match `json:"-"`
GoImportPath string `json:"-"`
GoPackage string `json:"-"`
GoTypeName string `json:"-"`
GoBasicType bool `json:"-"`
// Parsed form of GoStructTag, e.g. {"validate:", "required"}
GoStructTags map[string]string `json:"-"`
ShimOverride *ShimOverride `json:"-"`
}
func (o *Override) Matches(n *plugin.Identifier, defaultSchema string) bool {
if n == nil {
return false
}
schema := n.Schema
if n.Schema == "" {
schema = defaultSchema
}
if o.TableCatalog != nil && !o.TableCatalog.MatchString(n.Catalog) {
return false
}
if o.TableSchema == nil && schema != "" {
return false
}
if o.TableSchema != nil && !o.TableSchema.MatchString(schema) {
return false
}
if o.TableRel == nil && n.Name != "" {
return false
}
if o.TableRel != nil && !o.TableRel.MatchString(n.Name) {
return false
}
return true
}
func (o *Override) MatchesColumn(col *plugin.Column) bool {
columnType := sdk.DataType(col.Type)
notNull := col.NotNull || col.IsArray
return o.DBType != "" && o.DBType == columnType && o.Nullable != notNull && o.Unsigned == col.Unsigned
}
func (o *Override) parse(req *plugin.GenerateRequest) (err error) {
// validate deprecated postgres_type field
if o.Deprecated_PostgresType != "" {
fmt.Fprintf(os.Stderr, "WARNING: \"postgres_type\" is deprecated. Instead, use \"db_type\" to specify a type override.\n")
if o.DBType != "" {
return fmt.Errorf(`Type override configurations cannot have "db_type" and "postres_type" together. Use "db_type" alone`)
}
o.DBType = o.Deprecated_PostgresType
}
// validate deprecated null field
if o.Deprecated_Null {
fmt.Fprintf(os.Stderr, "WARNING: \"null\" is deprecated. Instead, use the \"nullable\" field.\n")
o.Nullable = true
}
schema := "public"
if req != nil && req.Catalog != nil {
schema = req.Catalog.DefaultSchema
}
// validate option combinations
switch {
case o.Column != "" && o.DBType != "":
return fmt.Errorf("Override specifying both `column` (%q) and `db_type` (%q) is not valid.", o.Column, o.DBType)
case o.Column == "" && o.DBType == "":
return fmt.Errorf("Override must specify one of either `column` or `db_type`")
}
// validate Column
if o.Column != "" {
colParts := strings.Split(o.Column, ".")
switch len(colParts) {
case 2:
if o.ColumnName, err = pattern.MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableRel, err = pattern.MatchCompile(colParts[0]); err != nil {
return err
}
if o.TableSchema, err = pattern.MatchCompile(schema); err != nil {
return err
}
case 3:
if o.ColumnName, err = pattern.MatchCompile(colParts[2]); err != nil {
return err
}
if o.TableRel, err = pattern.MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableSchema, err = pattern.MatchCompile(colParts[0]); err != nil {
return err
}
case 4:
if o.ColumnName, err = pattern.MatchCompile(colParts[3]); err != nil {
return err
}
if o.TableRel, err = pattern.MatchCompile(colParts[2]); err != nil {
return err
}
if o.TableSchema, err = pattern.MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableCatalog, err = pattern.MatchCompile(colParts[0]); err != nil {
return err
}
default:
return fmt.Errorf("Override `column` specifier %q is not the proper format, expected '[catalog.][schema.]tablename.colname'", o.Column)
}
}
// validate GoType
parsed, err := o.GoType.parse()
if err != nil {
return err
}
o.GoImportPath = parsed.ImportPath
o.GoPackage = parsed.Package
o.GoTypeName = parsed.TypeName
o.GoBasicType = parsed.BasicType
// validate GoStructTag
tags, err := o.GoStructTag.parse()
if err != nil {
return err
}
o.GoStructTags = tags
o.ShimOverride = shimOverride(req, o)
return nil
}

View File

@@ -0,0 +1,117 @@
package opts
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestTypeOverrides(t *testing.T) {
for _, test := range []struct {
override Override
pkg string
typeName string
basic bool
}{
{
Override{
DBType: "uuid",
GoType: GoType{Spec: "github.com/segmentio/ksuid.KSUID"},
},
"github.com/segmentio/ksuid",
"ksuid.KSUID",
false,
},
// TODO: Add test for struct pointers
//
// {
// Override{
// DBType: "uuid",
// GoType: "github.com/segmentio/*ksuid.KSUID",
// },
// "github.com/segmentio/ksuid",
// "*ksuid.KSUID",
// false,
// },
{
Override{
DBType: "citext",
GoType: GoType{Spec: "string"},
},
"",
"string",
true,
},
{
Override{
DBType: "timestamp",
GoType: GoType{Spec: "time.Time"},
},
"time",
"time.Time",
false,
},
} {
tt := test
t.Run(tt.override.GoType.Spec, func(t *testing.T) {
if err := tt.override.parse(nil); err != nil {
t.Fatalf("override parsing failed; %s", err)
}
if diff := cmp.Diff(tt.pkg, tt.override.GoImportPath); diff != "" {
t.Errorf("package mismatch;\n%s", diff)
}
if diff := cmp.Diff(tt.typeName, tt.override.GoTypeName); diff != "" {
t.Errorf("type name mismatch;\n%s", diff)
}
if diff := cmp.Diff(tt.basic, tt.override.GoBasicType); diff != "" {
t.Errorf("basic mismatch;\n%s", diff)
}
})
}
for _, test := range []struct {
override Override
err string
}{
{
Override{
DBType: "uuid",
GoType: GoType{Spec: "Pointer"},
},
"Package override `go_type` specifier \"Pointer\" is not a Go basic type e.g. 'string'",
},
{
Override{
DBType: "uuid",
GoType: GoType{Spec: "untyped rune"},
},
"Package override `go_type` specifier \"untyped rune\" is not a Go basic type e.g. 'string'",
},
} {
tt := test
t.Run(tt.override.GoType.Spec, func(t *testing.T) {
err := tt.override.parse(nil)
if err == nil {
t.Fatalf("expected parse to fail; got nil")
}
if diff := cmp.Diff(tt.err, err.Error()); diff != "" {
t.Errorf("error mismatch;\n%s", diff)
}
})
}
}
func FuzzOverride(f *testing.F) {
for _, spec := range []string{
"string",
"github.com/gofrs/uuid.UUID",
"github.com/segmentio/ksuid.KSUID",
} {
f.Add(spec)
}
f.Fuzz(func(t *testing.T, s string) {
o := Override{
GoType: GoType{Spec: s},
}
o.parse(nil)
})
}

View File

@@ -0,0 +1,77 @@
package opts
import (
"strings"
"github.com/sqlc-dev/sqlc/internal/plugin"
)
// The ShimOverride struct exists to bridge the gap between the Override struct
// and the previous Override struct defined in codegen.proto. Eventually these
// shim structs should be removed in favor of using the existing Override and
// GoType structs, but it's easier to provide these shim structs to not change
// the existing, working code.
type ShimOverride struct {
DbType string
Nullable bool
Column string
Table *plugin.Identifier
ColumnName string
Unsigned bool
GoType *ShimGoType
}
func shimOverride(req *plugin.GenerateRequest, o *Override) *ShimOverride {
var column string
var table plugin.Identifier
if o.Column != "" {
colParts := strings.Split(o.Column, ".")
switch len(colParts) {
case 2:
table.Schema = req.Catalog.DefaultSchema
table.Name = colParts[0]
column = colParts[1]
case 3:
table.Schema = colParts[0]
table.Name = colParts[1]
column = colParts[2]
case 4:
table.Catalog = colParts[0]
table.Schema = colParts[1]
table.Name = colParts[2]
column = colParts[3]
}
}
return &ShimOverride{
DbType: o.DBType,
Nullable: o.Nullable,
Unsigned: o.Unsigned,
Column: o.Column,
ColumnName: column,
Table: &table,
GoType: shimGoType(o),
}
}
type ShimGoType struct {
ImportPath string
Package string
TypeName string
BasicType bool
StructTags map[string]string
}
func shimGoType(o *Override) *ShimGoType {
// Note that there is a slight mismatch between this and the
// proto api. The GoType on the override is the unparsed type,
// which could be a qualified path or an object, as per
// https://docs.sqlc.dev/en/v1.18.0/reference/config.html#type-overriding
return &ShimGoType{
ImportPath: o.GoImportPath,
Package: o.GoPackage,
TypeName: o.GoTypeName,
BasicType: o.GoBasicType,
StructTags: o.GoStructTags,
}
}