From ac19b552e5ffe5d5fbc3171acbecae844f6e73b0 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sat, 14 Apr 2018 17:55:05 +0100 Subject: [PATCH] Add SQL RUN query statement type --- db/run.go | 39 ++++++++++++++++++ db/run_test.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ sql/ast.go | 10 +++++ sql/cork.go | 82 ++++++++++++++++++++++++++------------ sql/run.go | 31 +++++++++++++++ sql/string.go | 6 +++ sql/tokens.go | 2 + 7 files changed, 250 insertions(+), 26 deletions(-) create mode 100644 db/run.go create mode 100644 db/run_test.go create mode 100644 sql/run.go diff --git a/db/run.go b/db/run.go new file mode 100644 index 00000000..42d3d2bc --- /dev/null +++ b/db/run.go @@ -0,0 +1,39 @@ +// 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/sql" +) + +func (e *executor) executeRun(ctx context.Context, stm *sql.RunStatement) (out []interface{}, err error) { + + val, err := e.fetch(ctx, stm, nil) + if err != nil { + return nil, err + } + + switch val := val.(type) { + case []interface{}: + out = val + case interface{}: + out = append(out, val) + } + + return + +} diff --git a/db/run_test.go b/db/run_test.go new file mode 100644 index 00000000..47759177 --- /dev/null +++ b/db/run_test.go @@ -0,0 +1,106 @@ +// 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 ( + "fmt" + "sync" + "testing" + "time" + + "net/http" + "net/http/httptest" + + . "github.com/smartystreets/goconvey/convey" +) + +type Handler struct { + sync.Mutex + count int +} + +func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.Lock() + defer s.Unlock() + s.count++ + fmt.Fprintf(w, "SERVER OK: %d", s.count) +} + +func TestRun(t *testing.T) { + + handler := &Handler{} + server := httptest.NewServer(handler) + defer server.Close() + + Convey("Run statement which runs http requests", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE TABLE test PERMISSIONS FULL; + DEFINE TABLE temp PERMISSIONS FOR SELECT FULL; + DEFINE EVENT done ON test WHEN true THEN ( + CREATE temp:main; + RUN http.get("` + server.URL + `"); + RUN http.put("` + server.URL + `"); + RUN http.post("` + server.URL + `"); + RUN http.delete("` + server.URL + `"); + RUN http.async.get("` + server.URL + `"); + RUN http.async.put("` + server.URL + `"); + RUN http.async.post("` + server.URL + `"); + RUN http.async.delete("` + server.URL + `"); + ); + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Status, ShouldEqual, "OK") + So(res[2].Status, ShouldEqual, "OK") + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM test, temp; + CREATE test:main; + SELECT * FROM test, temp; + ` + + res, err := Execute(setupSC(), txt, nil) + time.Sleep(1 * time.Second) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + So(res[1].Status, ShouldEqual, "OK") + So(res[1].Result, ShouldHaveLength, 0) + So(res[2].Status, ShouldEqual, "OK") + So(res[2].Result, ShouldHaveLength, 1) + So(res[3].Status, ShouldEqual, "OK") + So(res[3].Result, ShouldHaveLength, 2) + + handler.Lock() + So(handler.count, ShouldEqual, 8) + handler.Unlock() + + }() + + }) + +} diff --git a/sql/ast.go b/sql/ast.go index 646918df..c8a232fd 100644 --- a/sql/ast.go +++ b/sql/ast.go @@ -101,6 +101,16 @@ type IfStatement struct { Else Expr `cork:"else" codec:"else"` } +// -------------------------------------------------- +// Run +// -------------------------------------------------- + +// RunStatement represents a run clause. +type RunStatement struct { + RW bool `cork:"-" codec:"-"` + Expr Expr `cork:"expr" codec:"expr"` +} + // -------------------------------------------------- // LET // -------------------------------------------------- diff --git a/sql/cork.go b/sql/cork.go index 4227f6f3..a34c9624 100644 --- a/sql/cork.go +++ b/sql/cork.go @@ -930,6 +930,36 @@ func (this *IfStatement) UnmarshalCORK(r *cork.Reader) (err error) { return } +// -------------------------------------------------- +// RunStatement +// -------------------------------------------------- + +func init() { + cork.Register(&RunStatement{}) +} + +func (this *RunStatement) Decode(src []byte) { + pack.Decode(src, this) +} + +func (this *RunStatement) Encode() (dst []byte) { + return pack.Encode(this) +} + +func (this *RunStatement) ExtendCORK() byte { + return 0x72 +} + +func (this *RunStatement) MarshalCORK(w *cork.Writer) (err error) { + w.EncodeAny(this.Expr) + return +} + +func (this *RunStatement) UnmarshalCORK(r *cork.Reader) (err error) { + r.DecodeAny(&this.Expr) + return +} + // -------------------------------------------------- // LiveStatement // -------------------------------------------------- @@ -947,7 +977,7 @@ func (this *LiveStatement) Encode() (dst []byte) { } func (this *LiveStatement) ExtendCORK() byte { - return 0x72 + return 0x73 } func (this *LiveStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -987,7 +1017,7 @@ func (this *SelectStatement) Encode() (dst []byte) { } func (this *SelectStatement) ExtendCORK() byte { - return 0x73 + return 0x74 } func (this *SelectStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1041,7 +1071,7 @@ func (this *CreateStatement) Encode() (dst []byte) { } func (this *CreateStatement) ExtendCORK() byte { - return 0x74 + return 0x75 } func (this *CreateStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1083,7 +1113,7 @@ func (this *UpdateStatement) Encode() (dst []byte) { } func (this *UpdateStatement) ExtendCORK() byte { - return 0x75 + return 0x76 } func (this *UpdateStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1127,7 +1157,7 @@ func (this *DeleteStatement) Encode() (dst []byte) { } func (this *DeleteStatement) ExtendCORK() byte { - return 0x76 + return 0x77 } func (this *DeleteStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1171,7 +1201,7 @@ func (this *RelateStatement) Encode() (dst []byte) { } func (this *RelateStatement) ExtendCORK() byte { - return 0x77 + return 0x78 } func (this *RelateStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1219,7 +1249,7 @@ func (this *InsertStatement) Encode() (dst []byte) { } func (this *InsertStatement) ExtendCORK() byte { - return 0x78 + return 0x79 } func (this *InsertStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1261,7 +1291,7 @@ func (this *UpsertStatement) Encode() (dst []byte) { } func (this *UpsertStatement) ExtendCORK() byte { - return 0x79 + return 0x80 } func (this *UpsertStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1303,7 +1333,7 @@ func (this *DefineNamespaceStatement) Encode() (dst []byte) { } func (this *DefineNamespaceStatement) ExtendCORK() byte { - return 0x80 + return 0x81 } func (this *DefineNamespaceStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1333,7 +1363,7 @@ func (this *RemoveNamespaceStatement) Encode() (dst []byte) { } func (this *RemoveNamespaceStatement) ExtendCORK() byte { - return 0x81 + return 0x82 } func (this *RemoveNamespaceStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1363,7 +1393,7 @@ func (this *DefineDatabaseStatement) Encode() (dst []byte) { } func (this *DefineDatabaseStatement) ExtendCORK() byte { - return 0x82 + return 0x83 } func (this *DefineDatabaseStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1393,7 +1423,7 @@ func (this *RemoveDatabaseStatement) Encode() (dst []byte) { } func (this *RemoveDatabaseStatement) ExtendCORK() byte { - return 0x83 + return 0x84 } func (this *RemoveDatabaseStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1423,7 +1453,7 @@ func (this *DefineLoginStatement) Encode() (dst []byte) { } func (this *DefineLoginStatement) ExtendCORK() byte { - return 0x84 + return 0x85 } func (this *DefineLoginStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1459,7 +1489,7 @@ func (this *RemoveLoginStatement) Encode() (dst []byte) { } func (this *RemoveLoginStatement) ExtendCORK() byte { - return 0x85 + return 0x86 } func (this *RemoveLoginStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1491,7 +1521,7 @@ func (this *DefineTokenStatement) Encode() (dst []byte) { } func (this *DefineTokenStatement) ExtendCORK() byte { - return 0x86 + return 0x87 } func (this *DefineTokenStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1527,7 +1557,7 @@ func (this *RemoveTokenStatement) Encode() (dst []byte) { } func (this *RemoveTokenStatement) ExtendCORK() byte { - return 0x87 + return 0x88 } func (this *RemoveTokenStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1559,7 +1589,7 @@ func (this *DefineScopeStatement) Encode() (dst []byte) { } func (this *DefineScopeStatement) ExtendCORK() byte { - return 0x88 + return 0x89 } func (this *DefineScopeStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1603,7 +1633,7 @@ func (this *RemoveScopeStatement) Encode() (dst []byte) { } func (this *RemoveScopeStatement) ExtendCORK() byte { - return 0x89 + return 0x90 } func (this *RemoveScopeStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1633,7 +1663,7 @@ func (this *DefineTableStatement) Encode() (dst []byte) { } func (this *DefineTableStatement) ExtendCORK() byte { - return 0x90 + return 0x91 } func (this *DefineTableStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1679,7 +1709,7 @@ func (this *RemoveTableStatement) Encode() (dst []byte) { } func (this *RemoveTableStatement) ExtendCORK() byte { - return 0x91 + return 0x92 } func (this *RemoveTableStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1709,7 +1739,7 @@ func (this *DefineEventStatement) Encode() (dst []byte) { } func (this *DefineEventStatement) ExtendCORK() byte { - return 0x92 + return 0x93 } func (this *DefineEventStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1745,7 +1775,7 @@ func (this *RemoveEventStatement) Encode() (dst []byte) { } func (this *RemoveEventStatement) ExtendCORK() byte { - return 0x93 + return 0x94 } func (this *RemoveEventStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1777,7 +1807,7 @@ func (this *DefineFieldStatement) Encode() (dst []byte) { } func (this *DefineFieldStatement) ExtendCORK() byte { - return 0x94 + return 0x95 } func (this *DefineFieldStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1821,7 +1851,7 @@ func (this *RemoveFieldStatement) Encode() (dst []byte) { } func (this *RemoveFieldStatement) ExtendCORK() byte { - return 0x95 + return 0x96 } func (this *RemoveFieldStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1853,7 +1883,7 @@ func (this *DefineIndexStatement) Encode() (dst []byte) { } func (this *DefineIndexStatement) ExtendCORK() byte { - return 0x96 + return 0x97 } func (this *DefineIndexStatement) MarshalCORK(w *cork.Writer) (err error) { @@ -1889,7 +1919,7 @@ func (this *RemoveIndexStatement) Encode() (dst []byte) { } func (this *RemoveIndexStatement) ExtendCORK() byte { - return 0x97 + return 0x98 } func (this *RemoveIndexStatement) MarshalCORK(w *cork.Writer) (err error) { diff --git a/sql/run.go b/sql/run.go new file mode 100644 index 00000000..85359dd3 --- /dev/null +++ b/sql/run.go @@ -0,0 +1,31 @@ +// 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 sql + +func (p *parser) parseRunStatement() (stmt *RunStatement, err error) { + + stmt = &RunStatement{} + + stmt.Expr, err = p.parseExpr() + + // If this query has any subqueries which + // need to alter the database then mark + // this query as a writeable statement. + + stmt.RW = p.buf.rw + + return + +} diff --git a/sql/string.go b/sql/string.go index fc112348..0a427469 100644 --- a/sql/string.go +++ b/sql/string.go @@ -168,6 +168,12 @@ func (this InfoStatement) String() string { } } +func (this RunStatement) String() string { + return print("RUN %v", + this.Expr, + ) +} + func (this IfStatement) String() string { m := make([]string, len(this.Cond)) for k := range this.Cond { diff --git a/sql/tokens.go b/sql/tokens.go index c065cdbd..af6f4c80 100644 --- a/sql/tokens.go +++ b/sql/tokens.go @@ -169,6 +169,7 @@ const ( RELATE REMOVE RETURN + RUN SCHEMAFULL SCHEMALESS SCOPE @@ -339,6 +340,7 @@ var tokens = [...]string{ RELATE: "RELATE", REMOVE: "REMOVE", RETURN: "RETURN", + RUN: "RUN", SCHEMAFULL: "SCHEMAFULL", SCHEMALESS: "SCHEMALESS", SCOPE: "SCOPE",