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:
Tobie Morgan Hitchcock 2018-04-04 19:20:07 +01:00
parent 86a911b4a8
commit 0dc9ad339c
8 changed files with 109 additions and 32 deletions

View file

@ -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 {

View file

@ -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;
`

View file

@ -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...)

View file

@ -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)

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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,
},
},
},
}}},

View file

@ -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, "; "),
)
}