Skip to content
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

compiler: Support calling functions with defaults #635

Merged
merged 1 commit into from
Jul 29, 2020
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
14 changes: 7 additions & 7 deletions internal/codegen/golang/postgresql_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ func postgresType(r *compiler.Result, col *compiler.Column, settings config.Comb
notNull := col.NotNull || col.IsArray

switch columnType {
case "serial", "pg_catalog.serial4":
case "serial", "serial4", "pg_catalog.serial4":
if notNull {
return "int32"
}
return "sql.NullInt32"

case "bigserial", "pg_catalog.serial8":
case "bigserial", "serial8", "pg_catalog.serial8":
if notNull {
return "int64"
}
return "sql.NullInt64"

case "smallserial", "pg_catalog.serial2":
case "smallserial", "serial2", "pg_catalog.serial2":
return "int16"

case "integer", "int", "int4", "pg_catalog.int4":
Expand All @@ -43,19 +43,19 @@ func postgresType(r *compiler.Result, col *compiler.Column, settings config.Comb
case "smallint", "int2", "pg_catalog.int2":
return "int16"

case "float", "double precision", "pg_catalog.float8":
case "float", "double precision", "float8", "pg_catalog.float8":
if notNull {
return "float64"
}
return "sql.NullFloat64"

case "real", "pg_catalog.float4":
case "real", "float4", "pg_catalog.float4":
if notNull {
return "float32"
}
return "sql.NullFloat64" // TODO: Change to sql.NullFloat32 after updating the go.mod file

case "pg_catalog.numeric", "money":
case "numeric", "pg_catalog.numeric", "money":
// Since the Go standard library does not have a decimal type, lib/pq
// returns numerics as strings.
//
Expand Down Expand Up @@ -121,7 +121,7 @@ func postgresType(r *compiler.Result, col *compiler.Column, settings config.Comb
}
return "sql.NullString"

case "pg_catalog.interval":
case "interval", "pg_catalog.interval":
if notNull {
return "int64"
}
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/output_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, error) {
if res.Name != nil {
name = *res.Name
}
fun, err := qc.catalog.GetFuncN(rel, len(n.Args.Items))
fun, err := qc.catalog.ResolveFuncCall(n)
if err == nil {
cols = append(cols, &Column{Name: name, DataType: dataType(fun.ReturnType), NotNull: true})
} else {
Expand Down
45 changes: 31 additions & 14 deletions internal/compiler/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,25 @@ func resolveCatalogRefs(c *catalog.Catalog, rvs []*pg.RangeVar, args []paramRef,
}

case *ast.FuncCall:
fun, err := c.GetFuncN(n.Func, len(n.Args.Items))
fun, err := c.ResolveFuncCall(n)
if err != nil {
// Synthesize a function on the fly to avoid returning with an error
// for an unknown Postgres function (e.g. defined in an extension)
var args []*catalog.Argument
for range n.Args.Items {
args = append(args, &catalog.Argument{
Type: &ast.TypeName{Name: "any"},
})
}
// Synthesize a function on the fly to avoid returning with an error
// for an unknown Postgres function (e.g. defined in an extension)
fun = catalog.Function{
fun = &catalog.Function{
Name: n.Func.Name,
Args: args,
ReturnType: &ast.TypeName{Name: "any"},
}
}
for i, item := range n.Args.Items {
defaultName := fun.Name
funcName := fun.Name
var argName string
switch inode := item.(type) {
case *pg.ParamRef:
if inode.Number != ref.ref.Number {
Expand All @@ -210,11 +211,15 @@ func resolveCatalogRefs(c *catalog.Catalog, rvs []*pg.RangeVar, args []paramRef,
continue
}
if inode.Name != nil {
defaultName = *inode.Name
argName = *inode.Name
}
}

if fun.Args == nil {
defaultName := funcName
if argName != "" {
defaultName = argName
}
a = append(a, Parameter{
Number: ref.ref.Number,
Column: &Column{
Expand All @@ -225,19 +230,31 @@ func resolveCatalogRefs(c *catalog.Catalog, rvs []*pg.RangeVar, args []paramRef,
continue
}

if i >= len(fun.Args) {
return nil, fmt.Errorf("incorrect number of arguments to %s", fun.Name)
var paramName string
var paramType *ast.TypeName
if argName == "" {
paramName = fun.Args[i].Name
paramType = fun.Args[i].Type
} else {
paramName = argName
for _, arg := range fun.Args {
if arg.Name == argName {
paramType = arg.Type
}
}
if paramType == nil {
panic(fmt.Sprintf("named argument %s has no type", paramName))
}
}
arg := fun.Args[i]
name := arg.Name
if name == "" {
name = defaultName
if paramName == "" {
paramName = funcName
}

a = append(a, Parameter{
Number: ref.ref.Number,
Column: &Column{
Name: parameterName(ref.ref.Number, name),
DataType: dataType(arg.Type),
Name: parameterName(ref.ref.Number, paramName),
DataType: dataType(paramType),
NotNull: true,
},
})
Expand Down
16 changes: 8 additions & 8 deletions internal/endtoend/testdata/func_args/go/query.sql.go

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

6 changes: 3 additions & 3 deletions internal/endtoend/testdata/generate_series/go/query.sql.go

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.

29 changes: 29 additions & 0 deletions internal/endtoend/testdata/sql_syntax_calling_funcs/go/db.go

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.

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

29 changes: 29 additions & 0 deletions internal/endtoend/testdata/sql_syntax_calling_funcs/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- https://www.postgresql.org/docs/current/sql-syntax-calling-funcs.html
CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)
RETURNS text
AS
$$
SELECT CASE
WHEN $3 THEN UPPER($1 || ' ' || $2)
ELSE LOWER($1 || ' ' || $2)
END;
$$
LANGUAGE SQL IMMUTABLE STRICT;

-- name: PositionalNotation :one
SELECT concat_lower_or_upper('Hello', 'World', true);

-- name: PositionalNoDefaault :one
SELECT concat_lower_or_upper('Hello', 'World');

-- name: NamedNotation :one
SELECT concat_lower_or_upper(a => 'Hello', b => 'World');

-- name: NamedAnyOrder :one
SELECT concat_lower_or_upper(a => 'Hello', b => 'World', uppercase => true);

-- name: NamedOtherOrder :one
SELECT concat_lower_or_upper(a => 'Hello', uppercase => true, b => 'World');

-- name: MixedNotation :one
SELECT concat_lower_or_upper('Hello', 'World', uppercase => true);
12 changes: 12 additions & 0 deletions internal/endtoend/testdata/sql_syntax_calling_funcs/sqlc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"engine": "postgresql",
"path": "go",
"name": "querytest",
"schema": "query.sql",
"queries": "query.sql"
}
]
}
2 changes: 1 addition & 1 deletion internal/engine/postgresql/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "github.com/kyleconroy/sqlc/internal/sql/catalog"
func NewCatalog() *catalog.Catalog {
c := catalog.New("public")
c.Schemas = append(c.Schemas, pgTemp())
c.Schemas = append(c.Schemas, pgCatalog())
c.Schemas = append(c.Schemas, genPGCatalog())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit scary! I'm curious if we'll have to walk this specific change back.

c.SearchPath = []string{"pg_catalog"}
return c
}
Loading