From 8fadbc9f35ebe4744bc8993496f208649c0f4245 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Mon, 4 Dec 2017 19:08:41 +0000 Subject: [PATCH] Enable path expressions starting from params --- db/fetch.go | 45 ++++-- db/select_test.go | 362 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+), 9 deletions(-) diff --git a/db/fetch.go b/db/fetch.go index e8e0561b..b8d8ec52 100644 --- a/db/fetch.go +++ b/db/fetch.go @@ -57,17 +57,12 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o case doc != nil: doc.Fetch(func(key string, val interface{}) interface{} { - switch key { - case ctxKeyId: + switch res := val.(type) { + case *sql.Thing: + val, _ = e.fetchThing(ctx, res, doc) return val default: - switch res := val.(type) { - case *sql.Thing: - val, _ = e.fetchThing(ctx, res, doc) - return val - default: - return val - } + return val } }) @@ -78,15 +73,41 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o case *sql.Param: if obj, ok := ctx.Value(ctxKeySubs).(*data.Doc); ok { + + obj.Fetch(func(key string, val interface{}) interface{} { + switch res := val.(type) { + case *sql.Thing: + val, _ = e.fetchThing(ctx, res, doc) + return val + default: + return val + } + }) + if res := obj.Get(val.ID).Data(); res != nil { return e.fetch(ctx, res, doc) } + } + if obj, ok := ctx.Value(ctxKeyVars).(*data.Doc); ok { + + obj.Fetch(func(key string, val interface{}) interface{} { + switch res := val.(type) { + case *sql.Thing: + val, _ = e.fetchThing(ctx, res, doc) + return val + default: + return val + } + }) + if res := obj.Get(val.ID).Data(); res != nil { return e.fetch(ctx, res, doc) } + } + return nil, nil case *sql.IfStatement: @@ -208,6 +229,12 @@ func (e *executor) fetchPaths(ctx context.Context, doc *data.Doc, exprs ...sql.E } case *sql.PartExpression: switch val := val.Part.(type) { + case *sql.Param: + res, err := e.fetch(ctx, val, doc) + if err != nil { + return nil, err + } + return e.fetchPaths(ctx, data.Consume(res), exprs...) case *sql.Ident: res, err := e.fetch(ctx, val, doc) if err != nil { diff --git a/db/select_test.go b/db/select_test.go index c7b07f76..742bfc3a 100644 --- a/db/select_test.go +++ b/db/select_test.go @@ -451,6 +451,368 @@ func TestSelect(t *testing.T) { }) + Convey("Select $thing from a direct `thing` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, person:test AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldResemble, &sql.Thing{"person", "test"}) + + }) + + Convey("Select 'id' parameter from a direct `thing` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, person:test.id AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldResemble, &sql.Thing{"person", "test"}) + + }) + + Convey("Select 'name' parameter from a direct `thing` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, person:test.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.name' parameter from a direct `thing` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, person:test.id.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.id.id.name' parameter from a direct `thing` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, person:test.id.id.id.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select $param parameter from a direct `param` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + LET person = person:test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, $person.id AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 1) + So(data.Consume(res[4].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldResemble, &sql.Thing{"person", "test"}) + + }) + + Convey("Select 'id' parameter from a direct `param` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + LET person = person:test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, $person.id AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 1) + So(data.Consume(res[4].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldResemble, &sql.Thing{"person", "test"}) + + }) + + Convey("Select 'name' parameter from a direct `param` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + LET person = person:test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, $person.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 1) + So(data.Consume(res[4].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.name' parameter from a direct `param` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + LET person = person:test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, $person.id.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 1) + So(data.Consume(res[4].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.id.id.name' parameter from a direct `param` record fetch", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + LET person = person:test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, $person.id.id.id.name AS test FROM tester; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(res[4].Result, ShouldHaveLength, 1) + So(data.Consume(res[4].Result[0]).Get("meta.tb").Data(), ShouldEqual, "tester") + So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select $parent parameter from a subquery `param`", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, (SELECT $parent FROM tester LIMIT 1) AS test FROM person; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Tobias") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldResemble, map[string]interface{}{ + "id": &sql.Thing{"person", "test"}, + "meta": map[string]interface{}{ + "id": "test", + "tb": "person", + }, + "name": "Tobias", + }) + + }) + + Convey("Select 'id' parameter from a subquery `param`", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, (SELECT $parent.id FROM tester LIMIT 1) AS test FROM person; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Tobias") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldResemble, &sql.Thing{"person", "test"}) + + }) + + Convey("Select 'name' parameter from a subquery `param`", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, (SELECT $parent.name FROM tester LIMIT 1) AS test FROM person; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Tobias") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.name' parameter from a subquery `param`", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, (SELECT $parent.id.name FROM tester LIMIT 1) AS test FROM person; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Tobias") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + + Convey("Select 'id.id.id.name' parameter from a subquery `param`", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE tester:test; + CREATE person:test SET name="Tobias"; + SELECT *, (SELECT $parent.id.id.id.name FROM tester LIMIT 1) AS test FROM person; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Result, ShouldHaveLength, 1) + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Result, ShouldHaveLength, 1) + So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person") + So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test") + So(data.Consume(res[3].Result[0]).Get("name").Data(), ShouldEqual, "Tobias") + So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "Tobias") + + }) + Convey("Filter using VOID to find records where the field is not set", t, func() { setupDB()