diff --git a/internal/dinosql/gen.go b/internal/dinosql/gen.go index 7179294742..30ebc908fc 100644 --- a/internal/dinosql/gen.go +++ b/internal/dinosql/gen.go @@ -589,6 +589,11 @@ func (r Result) goInnerType(col core.Column) string { case "inet": return "net.IP" + case "void": + // A void value always returns NULL. Since there is no built-in NULL + // value into the SQL package, we'll use sql.NullBool + return "sql.NullBool" + case "any": return "interface{}" diff --git a/internal/dinosql/parser.go b/internal/dinosql/parser.go index 247a524b53..1b979de45c 100644 --- a/internal/dinosql/parser.go +++ b/internal/dinosql/parser.go @@ -951,12 +951,15 @@ type limitOffset struct { nodeImpl } -func (p *paramSearch) Visit(node nodes.Node) Visitor { +func (p paramSearch) Visit(node nodes.Node) Visitor { switch n := node.(type) { case nodes.A_Expr: p.parent = node + case nodes.FuncCall: + p.parent = node + case nodes.InsertStmt: if s, ok := n.SelectStmt.(nodes.SelectStmt); ok { for i, item := range s.TargetList.Items { @@ -1044,7 +1047,7 @@ func (p *paramSearch) Visit(node nodes.Node) Visitor { } func findParameters(root nodes.Node) []paramRef { - v := ¶mSearch{refs: map[int]paramRef{}} + v := paramSearch{refs: map[int]paramRef{}} Walk(v, root) refs := make([]paramRef, 0) for _, r := range v.refs { @@ -1218,6 +1221,45 @@ func resolveCatalogRefs(c core.Catalog, rvs []nodes.RangeVar, args []paramRef) ( } } + case nodes.FuncCall: + fqn, err := catalog.ParseList(n.Funcname) + if err != nil { + return nil, err + } + fun, err := c.LookupFunctionN(fqn, len(n.Args.Items)) + if err != nil { + return nil, err + } + for i, item := range n.Args.Items { + pr, ok := item.(nodes.ParamRef) + if !ok { + continue + } + if pr.Number != ref.ref.Number { + continue + } + if fun.Arguments == nil { + a = append(a, Parameter{ + Number: ref.ref.Number, + Column: core.Column{ + Name: fun.Name, + DataType: "any", + }, + }) + } + if i >= len(fun.Arguments) { + return nil, fmt.Errorf("incorrect number of arguments to %s", fun.Name) + } + a = append(a, Parameter{ + Number: ref.ref.Number, + Column: core.Column{ + Name: fun.Arguments[i].Name, + DataType: fun.Arguments[i].DataType, + NotNull: true, + }, + }) + } + case nodes.ResTarget: if n.Name == nil { return nil, fmt.Errorf("nodes.ResTarget has nil name") diff --git a/internal/dinosql/query_test.go b/internal/dinosql/query_test.go index 3031640359..9a3aa8fea6 100644 --- a/internal/dinosql/query_test.go +++ b/internal/dinosql/query_test.go @@ -852,6 +852,21 @@ func TestQueries(t *testing.T) { }, }, }, + { + "pg_advisory_xact_lock", + ` + SELECT pg_advisory_xact_lock($1); + `, + Query{ + Columns: []core.Column{ + // TODO: NotNull should be false + {Name: "pg_advisory_xact_lock", DataType: "void", NotNull: true}, + }, + Params: []Parameter{ + {1, core.Column{Name: "key", DataType: "bigint", NotNull: true}}, + }, + }, + }, } { test := tc t.Run(test.name, func(t *testing.T) { diff --git a/internal/pg/catalog.go b/internal/pg/catalog.go index e83a313781..f4eec2d23a 100644 --- a/internal/pg/catalog.go +++ b/internal/pg/catalog.go @@ -129,6 +129,7 @@ type Function struct { Arguments []Argument // not recorded for builtins ReturnType string Comment string + Desc string } type Argument struct { diff --git a/internal/pg/functions_admin.go b/internal/pg/functions_admin.go new file mode 100644 index 0000000000..00495cd897 --- /dev/null +++ b/internal/pg/functions_admin.go @@ -0,0 +1,175 @@ +package pg + +// Advisory Lock Functions +// +// The functions shown in Table 9.95 manage advisory locks. For details about +// proper use of these functions, see Section 13.3.5. +// +// https://www.postgresql.org/docs/current/functions-admin.html +// +// Table 9.95. Advisory Lock Functions +func advisoryLockFunctions() []Function { + return []Function{ + { + Name: "pg_advisory_lock", + Desc: "Obtain exclusive session level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_lock", + Desc: "Obtain exclusive session level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + { + Name: "pg_advisory_lock_shared", + Desc: "Obtain shared session level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_lock_shared", + Desc: "Obtain shared session level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + { + Name: "pg_advisory_unlock", + Desc: "Release an exclusive session level advisory lock", + ReturnType: "bool", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_unlock", + Desc: "Release an exclusive session level advisory lock", + ReturnType: "bool", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + { + Name: "pg_advisory_unlock_all", + Desc: "Release all session level advisory locks held by the current session", + ReturnType: "void", + }, + { + Name: "pg_advisory_unlock_shared", + Desc: "Unlock a shared session level advisory lock", + ReturnType: "bool", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_unlock_shared", + Desc: "Unlock a shared session level advisory lock", + ReturnType: "bool", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, + } +} diff --git a/internal/pg/pg_catalog.go b/internal/pg/pg_catalog.go index 6c58646ba2..5c36f3e587 100644 --- a/internal/pg/pg_catalog.go +++ b/internal/pg/pg_catalog.go @@ -76,6 +76,9 @@ func pgCatalog() Schema { ReturnType: "bool", }, } + + fs = append(fs, advisoryLockFunctions()...) + s.Funcs = make(map[string][]Function, len(fs)) for _, f := range fs { s.Funcs[f.Name] = append(s.Funcs[f.Name], f)