Add support for cursors with multiple order by clauses

This commit is contained in:
Vikram Rangnekar
2020-02-19 10:22:44 +05:30
parent 3d3e5d9c2b
commit c33e93ab37
14 changed files with 454 additions and 226 deletions

View File

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
@ -18,31 +17,41 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
if v := ctx.Value(userIDProviderKey); v != nil {
return io.WriteString(w, v.(string))
}
return 0, errors.New("query requires variable $user_id_provider")
return 0, argErr("user_id_provider")
case "user_id":
if v := ctx.Value(userIDKey); v != nil {
return io.WriteString(w, v.(string))
}
return 0, errors.New("query requires variable $user_id")
return 0, argErr("user_id")
case "user_role":
if v := ctx.Value(userRoleKey); v != nil {
return io.WriteString(w, v.(string))
}
return 0, errors.New("query requires variable $user_role")
return 0, argErr("user_role")
}
fields := jsn.Get(vars, [][]byte{[]byte(tag)})
if len(fields) == 0 {
return 0, nil
return 0, argErr(tag)
}
v := fields[0].Value
if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' {
fields[0].Value = v[1 : len(v)-1]
}
if tag == "cursor" {
v1, err := decrypt(string(fields[0].Value))
if err != nil {
return 0, err
}
return w.Write(v1)
}
return w.Write(escQuote(fields[0].Value))
}
}
@ -63,27 +72,37 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
for i := range args {
av := args[i]
switch {
case bytes.Equal(av, []byte("user_id")):
if v := ctx.Value(userIDKey); v != nil {
vars[i] = v.(string)
} else {
return nil, errors.New("query requires variable $user_id")
return nil, argErr("user_id")
}
case bytes.Equal(av, []byte("user_id_provider")):
if v := ctx.Value(userIDProviderKey); v != nil {
vars[i] = v.(string)
} else {
return nil, errors.New("query requires variable $user_id_provider")
return nil, argErr("user_id_provider")
}
case bytes.Equal(av, []byte("user_role")):
if v := ctx.Value(userRoleKey); v != nil {
vars[i] = v.(string)
} else {
return nil, errors.New("query requires variable $user_role")
return nil, argErr("user_role")
}
case bytes.Equal(av, []byte("cursor")):
if v, ok := fields["cursor"]; ok && v[0] == '"' {
v1, err := decrypt(string(v[1 : len(v)-1]))
if err != nil {
return nil, err
}
vars[i] = v1
} else {
return nil, argErr("cursor")
}
default:
@ -96,11 +115,12 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
if err := json.Unmarshal(v, &val); err != nil {
return nil, err
}
vars[i] = val
}
} else {
return nil, fmt.Errorf("query requires variable $%s", string(av))
return nil, argErr(string(av))
}
}
}
@ -135,3 +155,7 @@ func escQuote(b []byte) []byte {
}
return buf.Bytes()
}
func argErr(name string) error {
return fmt.Errorf("query requires variable '%s' to be set", name)
}

View File

@ -193,6 +193,8 @@ func (c *coreContext) resolveSQL() ([]byte, *stmt, error) {
}
st := &stmts[0]
//fmt.Println(">", string(st.sql))
t := fasttemplate.New(st.sql, openVar, closeVar)
buf := &bytes.Buffer{}

View File

@ -32,11 +32,11 @@ func encryptCursor(qc *qcode.QCode, data []byte) ([]byte, error) {
for i, f := range from {
to[i].Key = f.Key
if f.Value[0] < '0' || f.Value[0] > '9' {
if f.Value[0] != '"' || f.Value[len(f.Value)-1] != '"' {
continue
}
v, err := crypto.Encrypt(f.Value, &internalKey)
v, err := crypto.Encrypt(f.Value[1:len(f.Value)-1], &internalKey)
if err != nil {
return nil, err
}

View File

@ -46,9 +46,8 @@ func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
}
pc := psql.NewCompiler(psql.Config{
Schema: schema,
Decryptor: decrypt,
Vars: c.DB.Vars,
Schema: schema,
Vars: c.DB.Vars,
})
return qc, pc, nil