Enable multiple expressions with database events
It is now possible to run multiple query expressions when an event on a table has occured. Query expressions can be separated with a semicolon, and will be run in the same transaction as the main query.
This commit is contained in:
parent
86a911b4a8
commit
0dc9ad339c
8 changed files with 109 additions and 32 deletions
|
@ -192,9 +192,9 @@ func (d *document) allow(ctx context.Context, when method) (ok bool, err error)
|
|||
// table, and executes them in name order.
|
||||
func (d *document) event(ctx context.Context, when method) (err error) {
|
||||
|
||||
// Get the index values specified
|
||||
// Get the event values specified
|
||||
// for this table, loop through
|
||||
// them, and compute the changes.
|
||||
// them, and compute the events.
|
||||
|
||||
evs, err := d.getEV()
|
||||
if err != nil {
|
||||
|
|
|
@ -378,22 +378,24 @@ func TestDefine(t *testing.T) {
|
|||
|
||||
txt := `
|
||||
USE NS test DB test;
|
||||
DEFINE EVENT test ON person WHEN test > 1000 THEN (CREATE temp);
|
||||
DEFINE EVENT test ON person WHEN test > 1000 THEN (CREATE temp; CREATE test);
|
||||
UPDATE person:test SET test = 1000;
|
||||
UPDATE person:test SET test = 4000;
|
||||
UPDATE person:test SET test = 2000;
|
||||
UPDATE person:test SET test = 6000;
|
||||
SELECT * FROM temp;
|
||||
SELECT * FROM test;
|
||||
`
|
||||
|
||||
res, err := Execute(setupKV(), txt, nil)
|
||||
So(err, ShouldBeNil)
|
||||
So(res, ShouldHaveLength, 7)
|
||||
So(res, ShouldHaveLength, 8)
|
||||
So(res[2].Result, ShouldHaveLength, 1)
|
||||
So(res[3].Result, ShouldHaveLength, 1)
|
||||
So(res[4].Result, ShouldHaveLength, 1)
|
||||
So(res[5].Result, ShouldHaveLength, 1)
|
||||
So(res[6].Result, ShouldHaveLength, 3)
|
||||
So(res[7].Result, ShouldHaveLength, 3)
|
||||
|
||||
})
|
||||
|
||||
|
@ -403,22 +405,24 @@ func TestDefine(t *testing.T) {
|
|||
|
||||
txt := `
|
||||
USE NS test DB test;
|
||||
DEFINE EVENT test ON person WHEN $before.test < $after.test THEN (CREATE temp);
|
||||
DEFINE EVENT test ON person WHEN $before.test < $after.test THEN (CREATE temp; CREATE test);
|
||||
UPDATE person:test SET test = 1000;
|
||||
UPDATE person:test SET test = 4000;
|
||||
UPDATE person:test SET test = 2000;
|
||||
UPDATE person:test SET test = 6000;
|
||||
SELECT * FROM temp;
|
||||
SELECT * FROM test;
|
||||
`
|
||||
|
||||
res, err := Execute(setupKV(), txt, nil)
|
||||
So(err, ShouldBeNil)
|
||||
So(res, ShouldHaveLength, 7)
|
||||
So(res, ShouldHaveLength, 8)
|
||||
So(res[2].Result, ShouldHaveLength, 1)
|
||||
So(res[3].Result, ShouldHaveLength, 1)
|
||||
So(res[4].Result, ShouldHaveLength, 1)
|
||||
So(res[5].Result, ShouldHaveLength, 1)
|
||||
So(res[6].Result, ShouldHaveLength, 2)
|
||||
So(res[7].Result, ShouldHaveLength, 2)
|
||||
|
||||
})
|
||||
|
||||
|
@ -428,22 +432,24 @@ func TestDefine(t *testing.T) {
|
|||
|
||||
txt := `
|
||||
USE NS test DB test;
|
||||
DEFINE EVENT test ON person WHEN $before.test < 5000 AND $after.test > 5000 THEN (CREATE temp);
|
||||
DEFINE EVENT test ON person WHEN $before.test < 5000 AND $after.test > 5000 THEN (CREATE temp; CREATE test);
|
||||
UPDATE person:test SET test = 1000;
|
||||
UPDATE person:test SET test = 4000;
|
||||
UPDATE person:test SET test = 2000;
|
||||
UPDATE person:test SET test = 6000;
|
||||
SELECT * FROM temp;
|
||||
SELECT * FROM test;
|
||||
`
|
||||
|
||||
res, err := Execute(setupKV(), txt, nil)
|
||||
So(err, ShouldBeNil)
|
||||
So(res, ShouldHaveLength, 7)
|
||||
So(res, ShouldHaveLength, 8)
|
||||
So(res[2].Result, ShouldHaveLength, 1)
|
||||
So(res[3].Result, ShouldHaveLength, 1)
|
||||
So(res[4].Result, ShouldHaveLength, 1)
|
||||
So(res[5].Result, ShouldHaveLength, 1)
|
||||
So(res[6].Result, ShouldHaveLength, 1)
|
||||
So(res[7].Result, ShouldHaveLength, 1)
|
||||
|
||||
})
|
||||
|
||||
|
@ -473,7 +479,7 @@ func TestDefine(t *testing.T) {
|
|||
|
||||
txt := `
|
||||
USE NS test DB test;
|
||||
DEFINE EVENT test ON person WHEN true THEN false;
|
||||
DEFINE EVENT test ON person WHEN true THEN (CREATE test);
|
||||
SELECT * FROM person;
|
||||
`
|
||||
|
||||
|
|
29
db/fetch.go
29
db/fetch.go
|
@ -180,6 +180,35 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
|||
return e.fetchUpsert(ctx, exp, doc)
|
||||
}
|
||||
|
||||
case *sql.MultExpression:
|
||||
|
||||
for _, exp := range val.Expr {
|
||||
|
||||
switch exp := exp.(type) {
|
||||
case *sql.SelectStatement:
|
||||
out, err = e.fetchSelect(ctx, exp, doc)
|
||||
case *sql.CreateStatement:
|
||||
out, err = e.fetchCreate(ctx, exp, doc)
|
||||
case *sql.UpdateStatement:
|
||||
out, err = e.fetchUpdate(ctx, exp, doc)
|
||||
case *sql.DeleteStatement:
|
||||
out, err = e.fetchDelete(ctx, exp, doc)
|
||||
case *sql.RelateStatement:
|
||||
out, err = e.fetchRelate(ctx, exp, doc)
|
||||
case *sql.InsertStatement:
|
||||
out, err = e.fetchInsert(ctx, exp, doc)
|
||||
case *sql.UpsertStatement:
|
||||
out, err = e.fetchUpsert(ctx, exp, doc)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
case *sql.PathExpression:
|
||||
|
||||
return e.fetchPaths(ctx, doc, val.Expr...)
|
||||
|
|
|
@ -114,7 +114,7 @@ func TestInfo(t *testing.T) {
|
|||
|
||||
txt := `
|
||||
USE NS test DB test;
|
||||
DEFINE EVENT test ON test WHEN true THEN null;
|
||||
DEFINE EVENT test ON test WHEN true THEN (CREATE test);
|
||||
DEFINE FIELD test ON test;
|
||||
DEFINE INDEX test ON test COLUMNS id;
|
||||
INFO FOR TABLE test;
|
||||
|
@ -132,7 +132,7 @@ func TestInfo(t *testing.T) {
|
|||
So(res[3].Status, ShouldEqual, "OK")
|
||||
So(res[4].Status, ShouldEqual, "OK")
|
||||
So(data.Consume(res[4].Result[0]).Get("event").Data(), ShouldHaveLength, 1)
|
||||
So(data.Consume(res[4].Result[0]).Get("event.test").Data(), ShouldEqual, "DEFINE EVENT test ON test WHEN true THEN NULL")
|
||||
So(data.Consume(res[4].Result[0]).Get("event.test").Data(), ShouldEqual, "DEFINE EVENT test ON test WHEN true THEN (CREATE test)")
|
||||
So(data.Consume(res[4].Result[0]).Get("field").Data(), ShouldHaveLength, 1)
|
||||
So(data.Consume(res[4].Result[0]).Get("field.test").Data(), ShouldEqual, "DEFINE FIELD test ON test")
|
||||
So(data.Consume(res[4].Result[0]).Get("index").Data(), ShouldHaveLength, 1)
|
||||
|
|
|
@ -46,7 +46,7 @@ func (p *parser) parseDefineEventStatement() (stmt *DefineEventStatement, err er
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Then, err = p.parseExpr(); err != nil {
|
||||
if stmt.Then, err = p.parseMult(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -605,10 +605,7 @@ func (p *parser) parseMult() (exp *MultExpression, err error) {
|
|||
|
||||
var stm Expr
|
||||
|
||||
tok, _, exi := p.mightBe(CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT)
|
||||
if !exi {
|
||||
break
|
||||
}
|
||||
tok, _, _ := p.mightBe(CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT)
|
||||
|
||||
switch tok {
|
||||
case CREATE:
|
||||
|
@ -633,7 +630,9 @@ func (p *parser) parseMult() (exp *MultExpression, err error) {
|
|||
|
||||
exp.Expr = append(exp.Expr, stm)
|
||||
|
||||
_, _, _ = p.mightBe(SEMICOLON)
|
||||
if _, _, exi := p.mightBe(SEMICOLON); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2595,7 +2595,7 @@ func Test_Parse_Queries_Define(t *testing.T) {
|
|||
},
|
||||
{
|
||||
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN`,
|
||||
err: "Found `` but expected `expression`",
|
||||
err: "Found `` but expected `(`",
|
||||
},
|
||||
{
|
||||
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN (UPDATE $this SET increased = true)`,
|
||||
|
@ -2608,18 +2608,61 @@ func Test_Parse_Queries_Define(t *testing.T) {
|
|||
Op: LT,
|
||||
RHS: &Param{"after.price"},
|
||||
},
|
||||
Then: &SubExpression{
|
||||
Expr: &UpdateStatement{
|
||||
KV: "*", NS: "*", DB: "*",
|
||||
What: Exprs{&Param{"this"}},
|
||||
Data: &DataExpression{[]*ItemExpression{
|
||||
{
|
||||
LHS: &Ident{"increased"},
|
||||
Op: EQ,
|
||||
RHS: true,
|
||||
},
|
||||
}},
|
||||
Echo: AFTER,
|
||||
Then: &MultExpression{
|
||||
Expr: []Expr{
|
||||
&UpdateStatement{
|
||||
KV: "*", NS: "*", DB: "*",
|
||||
What: Exprs{&Param{"this"}},
|
||||
Data: &DataExpression{[]*ItemExpression{
|
||||
{
|
||||
LHS: &Ident{"increased"},
|
||||
Op: EQ,
|
||||
RHS: true,
|
||||
},
|
||||
}},
|
||||
Echo: AFTER,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN (UPDATE $this SET increased = true; UPDATE $this SET finished = true)`,
|
||||
res: &Query{Statements: []Statement{&DefineEventStatement{
|
||||
KV: "*", NS: "*", DB: "*",
|
||||
Name: &Ident{"temp"},
|
||||
What: Tables{&Table{"person"}},
|
||||
When: &BinaryExpression{
|
||||
LHS: &Param{"before.price"},
|
||||
Op: LT,
|
||||
RHS: &Param{"after.price"},
|
||||
},
|
||||
Then: &MultExpression{
|
||||
Expr: []Expr{
|
||||
&UpdateStatement{
|
||||
KV: "*", NS: "*", DB: "*",
|
||||
What: Exprs{&Param{"this"}},
|
||||
Data: &DataExpression{[]*ItemExpression{
|
||||
{
|
||||
LHS: &Ident{"increased"},
|
||||
Op: EQ,
|
||||
RHS: true,
|
||||
},
|
||||
}},
|
||||
Echo: AFTER,
|
||||
},
|
||||
&UpdateStatement{
|
||||
KV: "*", NS: "*", DB: "*",
|
||||
What: Exprs{&Param{"this"}},
|
||||
Data: &DataExpression{[]*ItemExpression{
|
||||
{
|
||||
LHS: &Ident{"finished"},
|
||||
Op: EQ,
|
||||
RHS: true,
|
||||
},
|
||||
}},
|
||||
Echo: AFTER,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
|
|
|
@ -741,10 +741,10 @@ func (this SubExpression) String() string {
|
|||
func (this MultExpression) String() string {
|
||||
m := make([]string, len(this.Expr))
|
||||
for k := range this.Expr {
|
||||
m[k] = print("%v;", this.Expr[k])
|
||||
m[k] = print("%v", this.Expr[k])
|
||||
}
|
||||
return print("(%v)",
|
||||
strings.Join(m, " "),
|
||||
strings.Join(m, "; "),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue