Enable permissions on individual document fields
This commit is contained in:
parent
2a74759a71
commit
67cfca04b9
15 changed files with 515 additions and 169 deletions
29
db/check.go
29
db/check.go
|
@ -48,7 +48,7 @@ func (d *document) check(ctx context.Context, cond sql.Expr) (ok bool, err error
|
||||||
// Grant checks to see if the table permissions allow
|
// Grant checks to see if the table permissions allow
|
||||||
// this record to be accessed for live queries, and
|
// this record to be accessed for live queries, and
|
||||||
// if not then it errors accordingly.
|
// if not then it errors accordingly.
|
||||||
func (d *document) grant(ctx context.Context, when method) (ok bool, err error) {
|
func (d *document) grant(ctx context.Context, met method) (ok bool, err error) {
|
||||||
|
|
||||||
var val interface{}
|
var val interface{}
|
||||||
|
|
||||||
|
@ -86,16 +86,8 @@ func (d *document) grant(ctx context.Context, when method) (ok bool, err error)
|
||||||
// for this table, then because this is
|
// for this table, then because this is
|
||||||
// a scoped request, return an error.
|
// a scoped request, return an error.
|
||||||
|
|
||||||
switch p := tb.Perms.(type) {
|
if p, ok := tb.Perms.(*sql.PermExpression); ok {
|
||||||
case *sql.PermExpression:
|
val, err = d.i.e.fetch(ctx, p.Select, d.current)
|
||||||
switch when {
|
|
||||||
case _CREATE:
|
|
||||||
val, err = d.i.e.fetch(ctx, p.Select, d.current)
|
|
||||||
case _UPDATE:
|
|
||||||
val, err = d.i.e.fetch(ctx, p.Select, d.current)
|
|
||||||
case _DELETE:
|
|
||||||
val, err = d.i.e.fetch(ctx, p.Select, d.initial)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the permissions expressions
|
// If the permissions expressions
|
||||||
|
@ -103,8 +95,8 @@ func (d *document) grant(ctx context.Context, when method) (ok bool, err error)
|
||||||
// return this, dictating whether the
|
// return this, dictating whether the
|
||||||
// document is able to be viewed.
|
// document is able to be viewed.
|
||||||
|
|
||||||
if val, ok := val.(bool); ok {
|
if v, ok := val.(bool); ok {
|
||||||
return val, err
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise as this request is scoped,
|
// Otherwise as this request is scoped,
|
||||||
|
@ -118,7 +110,7 @@ func (d *document) grant(ctx context.Context, when method) (ok bool, err error)
|
||||||
// Query checks to see if the table permissions allow
|
// Query checks to see if the table permissions allow
|
||||||
// this record to be accessed for normal queries, and
|
// this record to be accessed for normal queries, and
|
||||||
// if not then it errors accordingly.
|
// if not then it errors accordingly.
|
||||||
func (d *document) allow(ctx context.Context, when method) (ok bool, err error) {
|
func (d *document) allow(ctx context.Context, met method) (ok bool, err error) {
|
||||||
|
|
||||||
var val interface{}
|
var val interface{}
|
||||||
|
|
||||||
|
@ -156,9 +148,8 @@ func (d *document) allow(ctx context.Context, when method) (ok bool, err error)
|
||||||
// for this table, then because this is
|
// for this table, then because this is
|
||||||
// a scoped request, return an error.
|
// a scoped request, return an error.
|
||||||
|
|
||||||
switch p := tb.Perms.(type) {
|
if p, ok := tb.Perms.(*sql.PermExpression); ok {
|
||||||
case *sql.PermExpression:
|
switch met {
|
||||||
switch when {
|
|
||||||
case _SELECT:
|
case _SELECT:
|
||||||
val, err = d.i.e.fetch(ctx, p.Select, d.current)
|
val, err = d.i.e.fetch(ctx, p.Select, d.current)
|
||||||
case _CREATE:
|
case _CREATE:
|
||||||
|
@ -175,8 +166,8 @@ func (d *document) allow(ctx context.Context, when method) (ok bool, err error)
|
||||||
// return this, dictating whether the
|
// return this, dictating whether the
|
||||||
// document is able to be viewed.
|
// document is able to be viewed.
|
||||||
|
|
||||||
if val, ok := val.(bool); ok {
|
if v, ok := val.(bool); ok {
|
||||||
return val, err
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise as this request is scoped,
|
// Otherwise as this request is scoped,
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (d *document) runCreate(ctx context.Context, stm *sql.CreateStatement) (int
|
||||||
return nil, &ExistError{exist: d.id}
|
return nil, &ExistError{exist: d.id}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.merge(ctx, stm.Data); err != nil {
|
if err = d.merge(ctx, met, stm.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,33 @@ func TestDefine(t *testing.T) {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Convert a schemaless to schemafull table, and ensure schemaless fields are still output", t, func() {
|
||||||
|
|
||||||
|
setupDB()
|
||||||
|
|
||||||
|
txt := `
|
||||||
|
USE NS test DB test;
|
||||||
|
DEFINE TABLE person SCHEMALESS;
|
||||||
|
UPDATE person:test SET test=true, other="text";
|
||||||
|
DEFINE TABLE person SCHEMAFULL;
|
||||||
|
DEFINE FIELD test ON person TYPE boolean;
|
||||||
|
SELECT * FROM person;
|
||||||
|
DEFINE FIELD other ON person TYPE string;
|
||||||
|
SELECT * FROM person;
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldHaveLength, 8)
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, true)
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, "text")
|
||||||
|
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, true)
|
||||||
|
So(data.Consume(res[5].Result[0]).Get("other").Data(), ShouldEqual, "text")
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("test").Data(), ShouldEqual, true)
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("other").Data(), ShouldEqual, "text")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Define a drop table", t, func() {
|
Convey("Define a drop table", t, func() {
|
||||||
|
|
||||||
setupDB()
|
setupDB()
|
||||||
|
@ -372,6 +399,101 @@ func TestDefine(t *testing.T) {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Specify the permissions of a field so that it is only visible to the correct authentication levels", t, func() {
|
||||||
|
|
||||||
|
setupDB()
|
||||||
|
|
||||||
|
func() {
|
||||||
|
|
||||||
|
txt := `
|
||||||
|
USE NS test DB test;
|
||||||
|
DEFINE TABLE person PERMISSIONS FULL;
|
||||||
|
DEFINE FIELD name ON person PERMISSIONS FULL;
|
||||||
|
DEFINE FIELD pass ON person PERMISSIONS NONE;
|
||||||
|
DEFINE FIELD test ON person PERMISSIONS FOR CREATE, UPDATE FULL FOR SELECT NONE;
|
||||||
|
DEFINE FIELD temp ON person PERMISSIONS NONE;
|
||||||
|
DEFINE FIELD temp.test ON person PERMISSIONS FULL;
|
||||||
|
UPDATE person:test SET name="Tobias", pass="qhmyjahdc4", test="k5n87urq8l", temp.test="zw3wf5ls39";
|
||||||
|
SELECT * FROM person;
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldHaveLength, 9)
|
||||||
|
So(res[7].Result, ShouldHaveLength, 1)
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("name").Data(), ShouldEqual, "Tobias")
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("pass").Data(), ShouldEqual, "qhmyjahdc4")
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("test").Data(), ShouldEqual, "k5n87urq8l")
|
||||||
|
So(data.Consume(res[7].Result[0]).Get("temp.test").Data(), ShouldEqual, "zw3wf5ls39")
|
||||||
|
So(res[8].Result, ShouldHaveLength, 1)
|
||||||
|
So(data.Consume(res[8].Result[0]).Get("name").Data(), ShouldEqual, "Tobias")
|
||||||
|
So(data.Consume(res[8].Result[0]).Get("pass").Data(), ShouldEqual, "qhmyjahdc4")
|
||||||
|
So(data.Consume(res[8].Result[0]).Get("test").Data(), ShouldEqual, "k5n87urq8l")
|
||||||
|
So(data.Consume(res[8].Result[0]).Get("temp.test").Data(), ShouldEqual, "zw3wf5ls39")
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
func() {
|
||||||
|
|
||||||
|
txt := `
|
||||||
|
USE NS test DB test;
|
||||||
|
CREATE person:1 SET name="Silvana", pass="1f65flhfvq", test="35aptguqoj", temp.test="h08ryx3519";
|
||||||
|
UPDATE person:2 SET name="Jonathan", pass="8k796m5mmj", test="1lzdhd6wzg", temp.test="xurnxp8a1e";
|
||||||
|
SELECT * FROM person ORDER BY name;
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := Execute(setupSC(), txt, nil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldHaveLength, 4)
|
||||||
|
So(res[1].Result, ShouldHaveLength, 1)
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("name").Data(), ShouldEqual, "Silvana")
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("temp.test").Data(), ShouldEqual, nil)
|
||||||
|
So(res[2].Result, ShouldHaveLength, 1)
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("name").Data(), ShouldEqual, "Jonathan")
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[2].Result[0]).Get("temp.test").Data(), ShouldEqual, nil)
|
||||||
|
So(res[3].Result, ShouldHaveLength, 3)
|
||||||
|
So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Jonathan")
|
||||||
|
So(data.Consume(res[3].Result[0]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[1]).Get("name").Data(), ShouldEqual, "Silvana")
|
||||||
|
So(data.Consume(res[3].Result[1]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[1]).Get("test").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[2]).Get("name").Data(), ShouldEqual, "Tobias")
|
||||||
|
So(data.Consume(res[3].Result[2]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[2]).Get("test").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[3].Result[2]).Get("temp.test").Data(), ShouldEqual, nil)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
func() {
|
||||||
|
|
||||||
|
txt := `
|
||||||
|
USE NS test DB test;
|
||||||
|
SELECT * FROM person ORDER BY name;
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldHaveLength, 2)
|
||||||
|
So(res[1].Result, ShouldHaveLength, 3)
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("name").Data(), ShouldEqual, "Jonathan")
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, "1lzdhd6wzg")
|
||||||
|
So(data.Consume(res[1].Result[1]).Get("name").Data(), ShouldEqual, "Silvana")
|
||||||
|
So(data.Consume(res[1].Result[1]).Get("pass").Data(), ShouldEqual, nil)
|
||||||
|
So(data.Consume(res[1].Result[1]).Get("test").Data(), ShouldEqual, "35aptguqoj")
|
||||||
|
So(data.Consume(res[1].Result[2]).Get("name").Data(), ShouldEqual, "Tobias")
|
||||||
|
So(data.Consume(res[1].Result[2]).Get("pass").Data(), ShouldEqual, "qhmyjahdc4")
|
||||||
|
So(data.Consume(res[1].Result[2]).Get("test").Data(), ShouldEqual, "k5n87urq8l")
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Define an event when a value changes", t, func() {
|
Convey("Define an event when a value changes", t, func() {
|
||||||
|
|
||||||
setupDB()
|
setupDB()
|
||||||
|
|
|
@ -282,15 +282,6 @@ func (d *document) changed() bool {
|
||||||
return len(c) > 0
|
return len(c) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) diff() *data.Doc {
|
|
||||||
a, _ := d.initial.Data().(map[string]interface{})
|
|
||||||
b, _ := d.current.Data().(map[string]interface{})
|
|
||||||
if c := diff.Diff(a, b); len(c) > 0 {
|
|
||||||
return data.Consume(c)
|
|
||||||
}
|
|
||||||
return data.Consume(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *document) shouldDrop() (bool, error) {
|
func (d *document) shouldDrop() (bool, error) {
|
||||||
|
|
||||||
// Check whether it is specified
|
// Check whether it is specified
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
|
|
||||||
// Event checks if any triggers are specified for this
|
// Event checks if any triggers are specified for this
|
||||||
// table, and executes them in name order.
|
// table, and executes them in name order.
|
||||||
func (d *document) event(ctx context.Context, when method) (err error) {
|
func (d *document) event(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
// Get the event values specified
|
// Get the event values specified
|
||||||
// for this table, loop through
|
// for this table, loop through
|
||||||
|
@ -38,7 +38,7 @@ func (d *document) event(ctx context.Context, when method) (err error) {
|
||||||
|
|
||||||
kind := ""
|
kind := ""
|
||||||
|
|
||||||
switch when {
|
switch met {
|
||||||
case _CREATE:
|
case _CREATE:
|
||||||
kind = "CREATE"
|
kind = "CREATE"
|
||||||
case _UPDATE:
|
case _UPDATE:
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (d *document) runInsert(ctx context.Context, stm *sql.InsertStatement) (int
|
||||||
return nil, &ExistError{exist: d.id}
|
return nil, &ExistError{exist: d.id}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.merge(ctx, nil); err != nil {
|
if err = d.merge(ctx, met, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
db/lives.go
41
db/lives.go
|
@ -18,7 +18,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/abcum/surreal/sql"
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/abcum/surreal/util/data"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lives checks if any table views are specified for
|
// Lives checks if any table views are specified for
|
||||||
|
@ -54,7 +53,7 @@ func (d *document) lives(ctx context.Context, when method) (err error) {
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var con *socket
|
var con *socket
|
||||||
var doc *data.Doc
|
var out interface{}
|
||||||
|
|
||||||
if con, ok = sockets[lv.FB]; ok {
|
if con, ok = sockets[lv.FB]; ok {
|
||||||
|
|
||||||
|
@ -98,7 +97,7 @@ func (d *document) lives(ctx context.Context, when method) (err error) {
|
||||||
|
|
||||||
case true:
|
case true:
|
||||||
|
|
||||||
doc = d.diff()
|
out, _ = d.yield(ctx, lv, sql.DIFF)
|
||||||
|
|
||||||
// If the query has projected fields which it
|
// If the query has projected fields which it
|
||||||
// wants to receive, then let's fetch these
|
// wants to receive, then let's fetch these
|
||||||
|
@ -106,39 +105,21 @@ func (d *document) lives(ctx context.Context, when method) (err error) {
|
||||||
|
|
||||||
case false:
|
case false:
|
||||||
|
|
||||||
for _, v := range lv.Expr {
|
out, _ = d.yield(ctx, lv, sql.ILLEGAL)
|
||||||
if _, ok := v.Expr.(*sql.All); ok {
|
|
||||||
doc = d.current
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if doc == nil {
|
|
||||||
doc = data.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range lv.Expr {
|
|
||||||
switch v := e.Expr.(type) {
|
|
||||||
case *sql.All:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
v, err := d.i.e.fetch(ctx, v, d.current)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
doc.Set(v, e.Field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch when {
|
switch when {
|
||||||
case _CREATE:
|
|
||||||
con.queue(id, lv.ID, "CREATE", doc.Data())
|
|
||||||
case _UPDATE:
|
|
||||||
con.queue(id, lv.ID, "UPDATE", doc.Data())
|
|
||||||
case _DELETE:
|
case _DELETE:
|
||||||
con.queue(id, lv.ID, "DELETE", d.id)
|
con.queue(id, lv.ID, "DELETE", d.id)
|
||||||
|
case _CREATE:
|
||||||
|
if out != nil {
|
||||||
|
con.queue(id, lv.ID, "CREATE", out)
|
||||||
|
}
|
||||||
|
case _UPDATE:
|
||||||
|
if out != nil {
|
||||||
|
con.queue(id, lv.ID, "UPDATE", out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
118
db/merge.go
118
db/merge.go
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/abcum/surreal/cnf"
|
||||||
"github.com/abcum/surreal/sql"
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/abcum/surreal/util/conv"
|
"github.com/abcum/surreal/util/conv"
|
||||||
"github.com/abcum/surreal/util/data"
|
"github.com/abcum/surreal/util/data"
|
||||||
|
@ -32,44 +33,44 @@ var main = map[string]struct{}{
|
||||||
"meta.id": {},
|
"meta.id": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) merge(ctx context.Context, data sql.Expr) (err error) {
|
func (d *document) merge(ctx context.Context, met method, data sql.Expr) (err error) {
|
||||||
|
|
||||||
if err = d.defFld(ctx); err != nil {
|
if err = d.defFld(ctx, met); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch expr := data.(type) {
|
switch expr := data.(type) {
|
||||||
case *sql.DataExpression:
|
case *sql.DataExpression:
|
||||||
if err = d.mrgSet(ctx, expr); err != nil {
|
if err = d.mrgSet(ctx, met, expr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *sql.DiffExpression:
|
case *sql.DiffExpression:
|
||||||
if err = d.mrgDpm(ctx, expr); err != nil {
|
if err = d.mrgDpm(ctx, met, expr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *sql.MergeExpression:
|
case *sql.MergeExpression:
|
||||||
if err = d.mrgAny(ctx, expr); err != nil {
|
if err = d.mrgAny(ctx, met, expr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *sql.ContentExpression:
|
case *sql.ContentExpression:
|
||||||
if err = d.mrgAll(ctx, expr); err != nil {
|
if err = d.mrgAll(ctx, met, expr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.defFld(ctx); err != nil {
|
if err = d.defFld(ctx, met); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.mrgFld(ctx); err != nil {
|
if err = d.mrgFld(ctx, met); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.defFld(ctx); err != nil {
|
if err = d.defFld(ctx, met); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.delFld(ctx); err != nil {
|
if err = d.delFld(ctx, met); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ func (d *document) merge(ctx context.Context, data sql.Expr) (err error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) defFld(ctx context.Context) (err error) {
|
func (d *document) defFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
d.current.Set(d.id, "id")
|
d.current.Set(d.id, "id")
|
||||||
d.current.Set(d.md, "meta")
|
d.current.Set(d.md, "meta")
|
||||||
|
@ -86,7 +87,7 @@ func (d *document) defFld(ctx context.Context) (err error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) delFld(ctx context.Context) (err error) {
|
func (d *document) delFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
tb, err := d.getTB()
|
tb, err := d.getTB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,7 +131,7 @@ func (d *document) delFld(ctx context.Context) (err error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) mrgAll(ctx context.Context, expr *sql.ContentExpression) (err error) {
|
func (d *document) mrgAll(ctx context.Context, met method, expr *sql.ContentExpression) (err error) {
|
||||||
|
|
||||||
var obj map[string]interface{}
|
var obj map[string]interface{}
|
||||||
|
|
||||||
|
@ -161,7 +162,7 @@ func (d *document) mrgAll(ctx context.Context, expr *sql.ContentExpression) (err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) mrgAny(ctx context.Context, expr *sql.MergeExpression) (err error) {
|
func (d *document) mrgAny(ctx context.Context, met method, expr *sql.MergeExpression) (err error) {
|
||||||
|
|
||||||
var obj map[string]interface{}
|
var obj map[string]interface{}
|
||||||
|
|
||||||
|
@ -190,7 +191,7 @@ func (d *document) mrgAny(ctx context.Context, expr *sql.MergeExpression) (err e
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) mrgDpm(ctx context.Context, expr *sql.DiffExpression) (err error) {
|
func (d *document) mrgDpm(ctx context.Context, met method, expr *sql.DiffExpression) (err error) {
|
||||||
|
|
||||||
var obj []interface{}
|
var obj []interface{}
|
||||||
var old map[string]interface{}
|
var old map[string]interface{}
|
||||||
|
@ -222,7 +223,7 @@ func (d *document) mrgDpm(ctx context.Context, expr *sql.DiffExpression) (err er
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) mrgSet(ctx context.Context, expr *sql.DataExpression) (err error) {
|
func (d *document) mrgSet(ctx context.Context, met method, expr *sql.DataExpression) (err error) {
|
||||||
|
|
||||||
for _, v := range expr.Data {
|
for _, v := range expr.Data {
|
||||||
|
|
||||||
|
@ -255,7 +256,7 @@ func (d *document) mrgSet(ctx context.Context, expr *sql.DataExpression) (err er
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *document) mrgFld(ctx context.Context) (err error) {
|
func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
fds, err := d.getFD()
|
fds, err := d.getFD()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -306,40 +307,95 @@ func (d *document) mrgFld(ctx context.Context) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the variables
|
|
||||||
|
|
||||||
vars.Set(val, varKeyValue)
|
|
||||||
vars.Set(val, varKeyAfter)
|
|
||||||
vars.Set(old, varKeyBefore)
|
|
||||||
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
|
||||||
|
|
||||||
// We are setting the value of the field
|
// We are setting the value of the field
|
||||||
|
|
||||||
if fd.Value != nil {
|
if fd.Value != nil {
|
||||||
|
|
||||||
|
// Reset the variables
|
||||||
|
|
||||||
|
vars.Set(val, varKeyValue)
|
||||||
|
vars.Set(val, varKeyAfter)
|
||||||
|
vars.Set(old, varKeyBefore)
|
||||||
|
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
||||||
|
|
||||||
if now, err := d.i.e.fetch(ctx, fd.Value, d.current); err != nil {
|
if now, err := d.i.e.fetch(ctx, fd.Value, d.current); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
val = now
|
val = now
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the variables
|
|
||||||
|
|
||||||
vars.Set(val, varKeyValue)
|
|
||||||
vars.Set(val, varKeyAfter)
|
|
||||||
vars.Set(old, varKeyBefore)
|
|
||||||
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
|
||||||
|
|
||||||
// We are checking the value of the field
|
// We are checking the value of the field
|
||||||
|
|
||||||
if fd.Assert != nil {
|
if fd.Assert != nil {
|
||||||
|
|
||||||
|
// Reset the variables
|
||||||
|
|
||||||
|
vars.Set(val, varKeyValue)
|
||||||
|
vars.Set(val, varKeyAfter)
|
||||||
|
vars.Set(old, varKeyBefore)
|
||||||
|
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
||||||
|
|
||||||
if chk, err := d.i.e.fetch(ctx, fd.Assert, d.current); err != nil {
|
if chk, err := d.i.e.fetch(ctx, fd.Assert, d.current); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if chk, ok := chk.(bool); ok && !chk {
|
} else if chk, ok := chk.(bool); ok && !chk {
|
||||||
return &FieldError{field: key, found: val, check: fd.Assert}
|
return &FieldError{field: key, found: val, check: fd.Assert}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are checking the permissions of the field
|
||||||
|
|
||||||
|
if fd.Perms != nil {
|
||||||
|
|
||||||
|
if k, ok := ctx.Value(ctxKeyKind).(cnf.Kind); ok {
|
||||||
|
if k > cnf.AuthDB {
|
||||||
|
|
||||||
|
// Reset the variables
|
||||||
|
|
||||||
|
vars.Set(val, varKeyValue)
|
||||||
|
vars.Set(val, varKeyAfter)
|
||||||
|
vars.Set(old, varKeyBefore)
|
||||||
|
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
||||||
|
|
||||||
|
switch p := fd.Perms.(type) {
|
||||||
|
case *sql.PermExpression:
|
||||||
|
switch met {
|
||||||
|
case _CREATE:
|
||||||
|
if v, err := d.i.e.fetch(ctx, p.Create, d.current); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if b, ok := v.(bool); !ok || !b {
|
||||||
|
val = old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _UPDATE:
|
||||||
|
if v, err := d.i.e.fetch(ctx, p.Update, d.current); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if b, ok := v.(bool); !ok || !b {
|
||||||
|
val = old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _DELETE:
|
||||||
|
if v, err := d.i.e.fetch(ctx, p.Delete, d.current); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if b, ok := v.(bool); !ok || !b {
|
||||||
|
val = old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are setting the value of the field
|
||||||
|
|
||||||
switch val.(type) {
|
switch val.(type) {
|
||||||
default:
|
default:
|
||||||
d.current.Iff(val, key)
|
d.current.Iff(val, key)
|
||||||
|
|
90
db/perms.go
Normal file
90
db/perms.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright © 2016 Abcum Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/abcum/surreal/cnf"
|
||||||
|
"github.com/abcum/surreal/sql"
|
||||||
|
"github.com/abcum/surreal/util/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) {
|
||||||
|
|
||||||
|
// Get the field definitions so we can
|
||||||
|
// check if the permissions allow us
|
||||||
|
// to view each field.
|
||||||
|
|
||||||
|
fds, err := d.getFD()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we have the table we reset the
|
||||||
|
// context to DB level so that no other
|
||||||
|
// embedded permissions are checked on
|
||||||
|
// records within these permissions.
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
|
||||||
|
|
||||||
|
// We then try to process the relevant
|
||||||
|
// permissions dependent on the query
|
||||||
|
// that we are currently processing. If
|
||||||
|
// there are no permissions specified
|
||||||
|
// for this table, then because this is
|
||||||
|
// a scoped request, return an error.
|
||||||
|
|
||||||
|
for _, fd := range fds {
|
||||||
|
|
||||||
|
if fd.Perms != nil {
|
||||||
|
|
||||||
|
err = doc.Walk(func(key string, val interface{}) error {
|
||||||
|
|
||||||
|
// We are checking the permissions of the field
|
||||||
|
|
||||||
|
if p, ok := fd.Perms.(*sql.PermExpression); ok {
|
||||||
|
|
||||||
|
// Get the old value
|
||||||
|
|
||||||
|
old := d.initial.Get(key).Data()
|
||||||
|
|
||||||
|
// Reset the variables
|
||||||
|
|
||||||
|
vars := data.New()
|
||||||
|
vars.Set(val, varKeyValue)
|
||||||
|
vars.Set(val, varKeyAfter)
|
||||||
|
vars.Set(old, varKeyBefore)
|
||||||
|
ctx = context.WithValue(ctx, ctxKeySpec, vars)
|
||||||
|
|
||||||
|
if v, err := d.i.e.fetch(ctx, p.Select, doc); err != nil {
|
||||||
|
return err
|
||||||
|
} else if b, ok := v.(bool); !ok || !b {
|
||||||
|
doc.Del(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}, fd.Name.ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
|
@ -106,7 +106,7 @@ func (d *document) runRelate(ctx context.Context, stm *sql.RelateStatement) (int
|
||||||
met = _UPDATE
|
met = _UPDATE
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.merge(ctx, stm.Data); err != nil {
|
if err = d.merge(ctx, met, stm.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
db/select.go
11
db/select.go
|
@ -137,6 +137,7 @@ func (d *document) runSelect(ctx context.Context, stm *sql.SelectStatement) (int
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var err error
|
var err error
|
||||||
|
var met = _SELECT
|
||||||
|
|
||||||
defer d.close()
|
defer d.close()
|
||||||
|
|
||||||
|
@ -148,12 +149,10 @@ func (d *document) runSelect(ctx context.Context, stm *sql.SelectStatement) (int
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.doc == nil {
|
if ok, err = d.allow(ctx, met); err != nil {
|
||||||
if ok, err = d.allow(ctx, _SELECT); err != nil {
|
return nil, err
|
||||||
return nil, err
|
} else if ok == false {
|
||||||
} else if ok == false {
|
return nil, nil
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, err = d.check(ctx, stm.Cond); err != nil {
|
if ok, err = d.check(ctx, stm.Cond); err != nil {
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/abcum/surreal/cnf"
|
|
||||||
"github.com/abcum/surreal/sql"
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/abcum/surreal/util/keys"
|
"github.com/abcum/surreal/util/keys"
|
||||||
)
|
)
|
||||||
|
@ -135,7 +134,7 @@ func (d *document) table(ctx context.Context, when method) (err error) {
|
||||||
func (d *document) tableDelete(ctx context.Context, id *sql.Thing, exp sql.Fields) (err error) {
|
func (d *document) tableDelete(ctx context.Context, id *sql.Thing, exp sql.Fields) (err error) {
|
||||||
|
|
||||||
stm := &sql.DeleteStatement{
|
stm := &sql.DeleteStatement{
|
||||||
KV: cnf.Settings.DB.Base,
|
KV: d.key.KV,
|
||||||
NS: d.key.NS,
|
NS: d.key.NS,
|
||||||
DB: d.key.DB,
|
DB: d.key.DB,
|
||||||
What: sql.Exprs{id},
|
What: sql.Exprs{id},
|
||||||
|
@ -163,7 +162,7 @@ func (d *document) tableUpdate(ctx context.Context, id *sql.Thing, exp sql.Field
|
||||||
}
|
}
|
||||||
|
|
||||||
stm := &sql.UpdateStatement{
|
stm := &sql.UpdateStatement{
|
||||||
KV: cnf.Settings.DB.Base,
|
KV: d.key.KV,
|
||||||
NS: d.key.NS,
|
NS: d.key.NS,
|
||||||
DB: d.key.DB,
|
DB: d.key.DB,
|
||||||
What: sql.Exprs{id},
|
What: sql.Exprs{id},
|
||||||
|
|
|
@ -125,7 +125,7 @@ func (d *document) runUpdate(ctx context.Context, stm *sql.UpdateStatement) (int
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.merge(ctx, stm.Data); err != nil {
|
if err = d.merge(ctx, met, stm.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (d *document) runUpsert(ctx context.Context, stm *sql.UpsertStatement) (int
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.merge(ctx, nil); err != nil {
|
if err = d.merge(ctx, met, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
245
db/yield.go
245
db/yield.go
|
@ -17,92 +17,209 @@ package db
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/abcum/surreal/cnf"
|
||||||
"github.com/abcum/surreal/sql"
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/abcum/surreal/util/data"
|
"github.com/abcum/surreal/util/data"
|
||||||
|
"github.com/abcum/surreal/util/diff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (d *document) cold(ctx context.Context) (doc *data.Doc, err error) {
|
||||||
|
|
||||||
|
// If we are authenticated using DB, NS,
|
||||||
|
// or KV permissions level, then we can
|
||||||
|
// return the document without copying.
|
||||||
|
|
||||||
|
if k, ok := ctx.Value(ctxKeyKind).(cnf.Kind); ok {
|
||||||
|
if k < cnf.AuthSC {
|
||||||
|
return d.initial, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we need to create a copy
|
||||||
|
// of the document so that we can add
|
||||||
|
// and remove fields before outputting.
|
||||||
|
|
||||||
|
doc = d.initial.Copy()
|
||||||
|
|
||||||
|
err = d.perms(ctx, doc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *document) cnow(ctx context.Context) (doc *data.Doc, err error) {
|
||||||
|
|
||||||
|
// If we are authenticated using DB, NS,
|
||||||
|
// or KV permissions level, then we can
|
||||||
|
// return the document without copying.
|
||||||
|
|
||||||
|
if k, ok := ctx.Value(ctxKeyKind).(cnf.Kind); ok {
|
||||||
|
if k < cnf.AuthSC {
|
||||||
|
return d.current, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we need to create a copy
|
||||||
|
// of the document so that we can add
|
||||||
|
// and remove fields before outputting.
|
||||||
|
|
||||||
|
doc = d.current.Copy()
|
||||||
|
|
||||||
|
err = d.perms(ctx, doc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *document) diffs(initial, current *data.Doc) *data.Doc {
|
||||||
|
|
||||||
|
a, _ := initial.Data().(map[string]interface{})
|
||||||
|
b, _ := current.Data().(map[string]interface{})
|
||||||
|
|
||||||
|
if c := diff.Diff(a, b); len(c) > 0 {
|
||||||
|
return data.Consume(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Consume(nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Token) (interface{}, error) {
|
func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Token) (interface{}, error) {
|
||||||
|
|
||||||
|
var exps sql.Fields
|
||||||
|
var grps sql.Groups
|
||||||
|
|
||||||
switch stm := stm.(type) {
|
switch stm := stm.(type) {
|
||||||
|
case *sql.LiveStatement:
|
||||||
|
exps = stm.Expr
|
||||||
case *sql.SelectStatement:
|
case *sql.SelectStatement:
|
||||||
|
exps = stm.Expr
|
||||||
|
grps = stm.Group
|
||||||
|
}
|
||||||
|
|
||||||
var doc *data.Doc
|
// If there are no field expressions
|
||||||
|
// then this was not a LIVE or SELECT
|
||||||
|
// query, and therefore the query will
|
||||||
|
// have an output format specified.
|
||||||
|
|
||||||
for _, v := range stm.Expr {
|
if len(exps) == 0 {
|
||||||
if _, ok := v.Expr.(*sql.All); ok {
|
|
||||||
doc = d.current
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if doc == nil {
|
|
||||||
doc = data.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range stm.Expr {
|
|
||||||
|
|
||||||
switch v := e.Expr.(type) {
|
|
||||||
case *sql.All:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
|
|
||||||
// If the query has a GROUP BY expression
|
|
||||||
// then let's check if this is an aggregate
|
|
||||||
// function, and if it is then pass the
|
|
||||||
// first argument directly through.
|
|
||||||
|
|
||||||
if len(stm.Group) > 0 {
|
|
||||||
if f, ok := e.Expr.(*sql.FuncExpression); ok && f.Aggr {
|
|
||||||
v, err := d.i.e.fetch(ctx, f.Args[0], d.current)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
doc.Set(v, f.String())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise treat the field normally, and
|
|
||||||
// calculate the value to be inserted into
|
|
||||||
// the final output document.
|
|
||||||
|
|
||||||
v, err := d.i.e.fetch(ctx, v, d.current)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v {
|
|
||||||
case d.current:
|
|
||||||
doc.Set(nil, e.Field)
|
|
||||||
default:
|
|
||||||
doc.Set(v, e.Field)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return doc.Data(), nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
switch output {
|
switch output {
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case sql.DIFF:
|
case sql.DIFF:
|
||||||
return d.diff().Data(), nil
|
|
||||||
|
old, err := d.cold(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
now, err := d.cnow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.diffs(old, now).Data(), nil
|
||||||
|
|
||||||
case sql.AFTER:
|
case sql.AFTER:
|
||||||
return d.current.Data(), nil
|
|
||||||
|
doc, err := d.cnow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return doc.Data(), nil
|
||||||
|
|
||||||
case sql.BEFORE:
|
case sql.BEFORE:
|
||||||
return d.initial.Data(), nil
|
|
||||||
|
doc, err := d.cold(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return doc.Data(), nil
|
||||||
|
|
||||||
case sql.BOTH:
|
case sql.BOTH:
|
||||||
|
|
||||||
|
old, err := d.cold(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
now, err := d.cnow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"after": d.current.Data(),
|
"after": now.Data(),
|
||||||
"before": d.initial.Data(),
|
"before": old.Data(),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// But if there are field expresions
|
||||||
|
// then this query is a LIVE or SELECT
|
||||||
|
// query, and we must output only the
|
||||||
|
// desired fields in the output.
|
||||||
|
|
||||||
|
var out = data.New()
|
||||||
|
|
||||||
|
doc, err := d.cnow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range exps {
|
||||||
|
if _, ok := e.Expr.(*sql.All); ok {
|
||||||
|
out = doc
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range exps {
|
||||||
|
|
||||||
|
switch v := e.Expr.(type) {
|
||||||
|
case *sql.All:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
|
||||||
|
// If the query has a GROUP BY expression
|
||||||
|
// then let's check if this is an aggregate
|
||||||
|
// function, and if it is then pass the
|
||||||
|
// first argument directly through.
|
||||||
|
|
||||||
|
if len(grps) > 0 {
|
||||||
|
if f, ok := e.Expr.(*sql.FuncExpression); ok && f.Aggr {
|
||||||
|
v, err := d.i.e.fetch(ctx, f.Args[0], doc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out.Set(v, f.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise treat the field normally, and
|
||||||
|
// calculate the value to be inserted into
|
||||||
|
// the final output document.
|
||||||
|
|
||||||
|
v, err := d.i.e.fetch(ctx, v, doc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v {
|
||||||
|
case doc:
|
||||||
|
out.Set(nil, e.Field)
|
||||||
|
default:
|
||||||
|
out.Set(v, e.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.Data(), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue