Skip to content

Override globs #1189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/codegen/golang/go_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ func goType(r *compiler.Result, col *compiler.Column, settings config.CombinedSe
if oride.GoTypeName == "" {
continue
}
sameTable := sameTableName(col.Table, oride.Table, r.Catalog.DefaultSchema)
if oride.Column != "" && oride.ColumnName == col.Name && sameTable {
sameTable := oride.Matches(col.Table, r.Catalog.DefaultSchema)
if oride.Column != "" && oride.ColumnName.MatchString(col.Name) && sameTable {
return oride.GoTypeName
}
}
Expand Down
4 changes: 2 additions & 2 deletions internal/codegen/python/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ func pyInnerType(r *compiler.Result, col *compiler.Column, settings config.Combi
if !oride.PythonType.IsSet() {
continue
}
sameTable := sameTableName(col.Table, oride.Table, r.Catalog.DefaultSchema)
if oride.Column != "" && oride.ColumnName == col.Name && sameTable {
sameTable := oride.Matches(col.Table, r.Catalog.DefaultSchema)
if oride.Column != "" && oride.ColumnName.MatchString(col.Name) && sameTable {
return oride.PythonType.TypeString()
}
if oride.DBType != "" && oride.DBType == col.DataType && oride.Nullable != (col.NotNull || col.IsArray) {
Expand Down
82 changes: 71 additions & 11 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"os"
"strings"

"github.com/kyleconroy/sqlc/internal/core"
"github.com/kyleconroy/sqlc/internal/sql/ast"

yaml "gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -165,15 +166,50 @@ type Override struct {
// fully qualified name of the column, e.g. `accounts.id`
Column string `json:"column" yaml:"column"`

ColumnName string
Table core.FQN
ColumnName *Match
TableCatalog *Match
TableSchema *Match
TableRel *Match
GoImportPath string
GoPackage string
GoTypeName string
GoBasicType bool
}

func (o *Override) Parse() error {
func (o *Override) Matches(n *ast.TableName, 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) Parse() (err error) {

// validate deprecated postgres_type field
if o.Deprecated_PostgresType != "" {
Expand Down Expand Up @@ -203,16 +239,40 @@ func (o *Override) Parse() error {
colParts := strings.Split(o.Column, ".")
switch len(colParts) {
case 2:
o.ColumnName = colParts[1]
o.Table = core.FQN{Schema: "public", Rel: colParts[0]}
if o.ColumnName, err = MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableRel, err = MatchCompile(colParts[0]); err != nil {
return err
}
if o.TableSchema, err = MatchCompile("public"); err != nil {
return err
}
case 3:
o.ColumnName = colParts[2]
o.Table = core.FQN{Schema: colParts[0], Rel: colParts[1]}
if o.ColumnName, err = MatchCompile(colParts[2]); err != nil {
return err
}
if o.TableRel, err = MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableSchema, err = MatchCompile(colParts[0]); err != nil {
return err
}
case 4:
o.ColumnName = colParts[3]
o.Table = core.FQN{Catalog: colParts[0], Schema: colParts[1], Rel: colParts[2]}
if o.ColumnName, err = MatchCompile(colParts[3]); err != nil {
return err
}
if o.TableRel, err = MatchCompile(colParts[2]); err != nil {
return err
}
if o.TableSchema, err = MatchCompile(colParts[1]); err != nil {
return err
}
if o.TableCatalog, err = MatchCompile(colParts[0]); err != nil {
return err
}
default:
return fmt.Errorf("Override `column` specifier %q is not the proper format, expected '[catalog.][schema.]colname.tablename'", o.Column)
return fmt.Errorf("Override `column` specifier %q is not the proper format, expected '[catalog.][schema.]tablename.colname'", o.Column)
}
}

Expand Down
57 changes: 57 additions & 0 deletions internal/config/match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package config

import (
"fmt"
"regexp"
)

// Match is a wrapper of *regexp.Regexp.
// It contains the match pattern compiled into a regular expression.
type Match struct {
*regexp.Regexp
}

// Compile takes our match expression as a string, and compiles it into a *Match object.
// Will return an error on an invalid pattern.
func MatchCompile(pattern string) (match *Match, err error) {
regex := ""
escaped := false
arr := []byte(pattern)

for i := 0; i < len(arr); i++ {
if escaped {
escaped = false
switch arr[i] {
case '*', '?', '\\':
regex += "\\" + string(arr[i])
default:
return nil, fmt.Errorf("Invalid escaped character '%c'", arr[i])
}
} else {
switch arr[i] {
case '\\':
escaped = true
case '*':
regex += ".*"
case '?':
regex += "."
case '.', '(', ')', '+', '|', '^', '$', '[', ']', '{', '}':
regex += "\\" + string(arr[i])
default:
regex += string(arr[i])
}
}
}

if escaped {
return nil, fmt.Errorf("Unterminated escape at end of pattern")
}

var r *regexp.Regexp

if r, err = regexp.Compile("^" + regex + "$"); err != nil {
return nil, err
}

return &Match{r}, nil
}
11 changes: 0 additions & 11 deletions internal/core/fqn.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,3 @@ type FQN struct {
Schema string
Rel string
}

func (f FQN) String() string {
s := f.Rel
if f.Schema != "" {
s = f.Schema + "." + s
}
if f.Catalog != "" {
s = f.Catalog + "." + s
}
return s
}
12 changes: 12 additions & 0 deletions internal/endtoend/testdata/overrides_go_types/mysql/go/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions internal/endtoend/testdata/overrides_go_types/mysql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ CREATE TABLE foo (
total bigint NOT NULL,
retyped text NOT NULL
);

CREATE TABLE bar (
other text NOT NULL,
total bigint NOT NULL,
also_retyped text NOT NULL
);

CREATE TABLE baz (
other text NOT NULL,
total bigint NOT NULL,
also_retyped text NOT NULL
);
4 changes: 4 additions & 0 deletions internal/endtoend/testdata/overrides_go_types/mysql/sqlc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
{
"go_type": "github.com/kyleconroy/sqlc-testdata/pkg.CustomType",
"column": "foo.retyped"
},
{
"go_type": "github.com/kyleconroy/sqlc-testdata/pkg.CustomType",
"column": "*.also_retyped"
}
]
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ CREATE TABLE foo (
bio text,
about text
);

CREATE TABLE bar (
id uuid NOT NULL,
other_id uuid NOT NULL,
more_id uuid NOT NULL,
age integer,
balance double,
bio text,
about text
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"queries": "query.sql",
"overrides": [
{
"column": "foo.id",
"column": "*.id",
"go_type": {
"import": "github.com/gofrs/uuid",
"package": "uuid",
"type": "UUID"
},
},
{
"column": "foo.other_id",
"column": "*.*_id",
"go_type": {
"import": "github.com/gofrs/uuid",
"package": "fuid",
Expand Down