From a76b51bcc0e3fe10196ad9d9006f230e19392948 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Sun, 22 Dec 2019 19:08:27 -0800 Subject: [PATCH 1/3] internal/dinosql: Implement advisory locks --- internal/dinosql/parser.go | 46 ++++++++++++++++++++++++++++++++-- internal/dinosql/query_test.go | 15 +++++++++++ internal/pg/catalog.go | 1 + internal/pg/pg_catalog.go | 31 +++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) 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/pg_catalog.go b/internal/pg/pg_catalog.go index 6c58646ba2..1c14178a47 100644 --- a/internal/pg/pg_catalog.go +++ b/internal/pg/pg_catalog.go @@ -75,6 +75,37 @@ func pgCatalog() Schema { ArgN: 1, ReturnType: "bool", }, + + // Table 9.95. Advisory Lock Functions + // https://www.postgresql.org/docs/current/functions-admin.html + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + ArgN: 1, + Arguments: []Argument{ + { + Name: "key", + DataType: "bigint", + }, + }, + }, + { + Name: "pg_advisory_xact_lock", + Desc: "Obtain exclusive transaction level advisory lock", + ReturnType: "void", + ArgN: 2, + Arguments: []Argument{ + { + Name: "key1", + DataType: "int", + }, + { + Name: "key1", + DataType: "int", + }, + }, + }, } s.Funcs = make(map[string][]Function, len(fs)) for _, f := range fs { From 63c99eea7fa0ad36b69be0301b319c6f648c6f47 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Sun, 22 Dec 2019 20:03:23 -0800 Subject: [PATCH 2/3] Support void Postgres type --- internal/dinosql/gen.go | 5 +++++ 1 file changed, 5 insertions(+) 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{}" From 985bfdb3a80c0ec9ab140b3135e44d544d348ec9 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Mon, 23 Dec 2019 17:04:56 -0800 Subject: [PATCH 3/3] Add more advisory lock functions --- internal/pg/functions_admin.go | 175 +++++++++++++++++++++++++++++++++ internal/pg/pg_catalog.go | 34 +------ 2 files changed, 178 insertions(+), 31 deletions(-) create mode 100644 internal/pg/functions_admin.go 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 1c14178a47..5c36f3e587 100644 --- a/internal/pg/pg_catalog.go +++ b/internal/pg/pg_catalog.go @@ -75,38 +75,10 @@ func pgCatalog() Schema { ArgN: 1, ReturnType: "bool", }, - - // Table 9.95. Advisory Lock Functions - // https://www.postgresql.org/docs/current/functions-admin.html - { - Name: "pg_advisory_xact_lock", - Desc: "Obtain exclusive transaction level advisory lock", - ReturnType: "void", - ArgN: 1, - Arguments: []Argument{ - { - Name: "key", - DataType: "bigint", - }, - }, - }, - { - Name: "pg_advisory_xact_lock", - Desc: "Obtain exclusive transaction level advisory lock", - ReturnType: "void", - ArgN: 2, - Arguments: []Argument{ - { - Name: "key1", - DataType: "int", - }, - { - Name: "key1", - DataType: "int", - }, - }, - }, } + + 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)