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.
|
// 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, when method) (err error) {
|
||||||
|
|
||||||
// Get the index values specified
|
// Get the event values specified
|
||||||
// for this table, loop through
|
// for this table, loop through
|
||||||
// them, and compute the changes.
|
// them, and compute the events.
|
||||||
|
|
||||||
evs, err := d.getEV()
|
evs, err := d.getEV()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -378,22 +378,24 @@ func TestDefine(t *testing.T) {
|
||||||
|
|
||||||
txt := `
|
txt := `
|
||||||
USE NS test DB test;
|
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 = 1000;
|
||||||
UPDATE person:test SET test = 4000;
|
UPDATE person:test SET test = 4000;
|
||||||
UPDATE person:test SET test = 2000;
|
UPDATE person:test SET test = 2000;
|
||||||
UPDATE person:test SET test = 6000;
|
UPDATE person:test SET test = 6000;
|
||||||
SELECT * FROM temp;
|
SELECT * FROM temp;
|
||||||
|
SELECT * FROM test;
|
||||||
`
|
`
|
||||||
|
|
||||||
res, err := Execute(setupKV(), txt, nil)
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(res, ShouldHaveLength, 7)
|
So(res, ShouldHaveLength, 8)
|
||||||
So(res[2].Result, ShouldHaveLength, 1)
|
So(res[2].Result, ShouldHaveLength, 1)
|
||||||
So(res[3].Result, ShouldHaveLength, 1)
|
So(res[3].Result, ShouldHaveLength, 1)
|
||||||
So(res[4].Result, ShouldHaveLength, 1)
|
So(res[4].Result, ShouldHaveLength, 1)
|
||||||
So(res[5].Result, ShouldHaveLength, 1)
|
So(res[5].Result, ShouldHaveLength, 1)
|
||||||
So(res[6].Result, ShouldHaveLength, 3)
|
So(res[6].Result, ShouldHaveLength, 3)
|
||||||
|
So(res[7].Result, ShouldHaveLength, 3)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -403,22 +405,24 @@ func TestDefine(t *testing.T) {
|
||||||
|
|
||||||
txt := `
|
txt := `
|
||||||
USE NS test DB test;
|
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 = 1000;
|
||||||
UPDATE person:test SET test = 4000;
|
UPDATE person:test SET test = 4000;
|
||||||
UPDATE person:test SET test = 2000;
|
UPDATE person:test SET test = 2000;
|
||||||
UPDATE person:test SET test = 6000;
|
UPDATE person:test SET test = 6000;
|
||||||
SELECT * FROM temp;
|
SELECT * FROM temp;
|
||||||
|
SELECT * FROM test;
|
||||||
`
|
`
|
||||||
|
|
||||||
res, err := Execute(setupKV(), txt, nil)
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(res, ShouldHaveLength, 7)
|
So(res, ShouldHaveLength, 8)
|
||||||
So(res[2].Result, ShouldHaveLength, 1)
|
So(res[2].Result, ShouldHaveLength, 1)
|
||||||
So(res[3].Result, ShouldHaveLength, 1)
|
So(res[3].Result, ShouldHaveLength, 1)
|
||||||
So(res[4].Result, ShouldHaveLength, 1)
|
So(res[4].Result, ShouldHaveLength, 1)
|
||||||
So(res[5].Result, ShouldHaveLength, 1)
|
So(res[5].Result, ShouldHaveLength, 1)
|
||||||
So(res[6].Result, ShouldHaveLength, 2)
|
So(res[6].Result, ShouldHaveLength, 2)
|
||||||
|
So(res[7].Result, ShouldHaveLength, 2)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -428,22 +432,24 @@ func TestDefine(t *testing.T) {
|
||||||
|
|
||||||
txt := `
|
txt := `
|
||||||
USE NS test DB test;
|
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 = 1000;
|
||||||
UPDATE person:test SET test = 4000;
|
UPDATE person:test SET test = 4000;
|
||||||
UPDATE person:test SET test = 2000;
|
UPDATE person:test SET test = 2000;
|
||||||
UPDATE person:test SET test = 6000;
|
UPDATE person:test SET test = 6000;
|
||||||
SELECT * FROM temp;
|
SELECT * FROM temp;
|
||||||
|
SELECT * FROM test;
|
||||||
`
|
`
|
||||||
|
|
||||||
res, err := Execute(setupKV(), txt, nil)
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(res, ShouldHaveLength, 7)
|
So(res, ShouldHaveLength, 8)
|
||||||
So(res[2].Result, ShouldHaveLength, 1)
|
So(res[2].Result, ShouldHaveLength, 1)
|
||||||
So(res[3].Result, ShouldHaveLength, 1)
|
So(res[3].Result, ShouldHaveLength, 1)
|
||||||
So(res[4].Result, ShouldHaveLength, 1)
|
So(res[4].Result, ShouldHaveLength, 1)
|
||||||
So(res[5].Result, ShouldHaveLength, 1)
|
So(res[5].Result, ShouldHaveLength, 1)
|
||||||
So(res[6].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 := `
|
txt := `
|
||||||
USE NS test DB test;
|
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;
|
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)
|
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:
|
case *sql.PathExpression:
|
||||||
|
|
||||||
return e.fetchPaths(ctx, doc, val.Expr...)
|
return e.fetchPaths(ctx, doc, val.Expr...)
|
||||||
|
|
|
@ -114,7 +114,7 @@ func TestInfo(t *testing.T) {
|
||||||
|
|
||||||
txt := `
|
txt := `
|
||||||
USE NS test DB test;
|
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 FIELD test ON test;
|
||||||
DEFINE INDEX test ON test COLUMNS id;
|
DEFINE INDEX test ON test COLUMNS id;
|
||||||
INFO FOR TABLE test;
|
INFO FOR TABLE test;
|
||||||
|
@ -132,7 +132,7 @@ func TestInfo(t *testing.T) {
|
||||||
So(res[3].Status, ShouldEqual, "OK")
|
So(res[3].Status, ShouldEqual, "OK")
|
||||||
So(res[4].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").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").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("field.test").Data(), ShouldEqual, "DEFINE FIELD test ON test")
|
||||||
So(data.Consume(res[4].Result[0]).Get("index").Data(), ShouldHaveLength, 1)
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.Then, err = p.parseExpr(); err != nil {
|
if stmt.Then, err = p.parseMult(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -605,10 +605,7 @@ func (p *parser) parseMult() (exp *MultExpression, err error) {
|
||||||
|
|
||||||
var stm Expr
|
var stm Expr
|
||||||
|
|
||||||
tok, _, exi := p.mightBe(CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT)
|
tok, _, _ := p.mightBe(CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT)
|
||||||
if !exi {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tok {
|
switch tok {
|
||||||
case CREATE:
|
case CREATE:
|
||||||
|
@ -633,7 +630,9 @@ func (p *parser) parseMult() (exp *MultExpression, err error) {
|
||||||
|
|
||||||
exp.Expr = append(exp.Expr, stm)
|
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`,
|
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)`,
|
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,
|
Op: LT,
|
||||||
RHS: &Param{"after.price"},
|
RHS: &Param{"after.price"},
|
||||||
},
|
},
|
||||||
Then: &SubExpression{
|
Then: &MultExpression{
|
||||||
Expr: &UpdateStatement{
|
Expr: []Expr{
|
||||||
KV: "*", NS: "*", DB: "*",
|
&UpdateStatement{
|
||||||
What: Exprs{&Param{"this"}},
|
KV: "*", NS: "*", DB: "*",
|
||||||
Data: &DataExpression{[]*ItemExpression{
|
What: Exprs{&Param{"this"}},
|
||||||
{
|
Data: &DataExpression{[]*ItemExpression{
|
||||||
LHS: &Ident{"increased"},
|
{
|
||||||
Op: EQ,
|
LHS: &Ident{"increased"},
|
||||||
RHS: true,
|
Op: EQ,
|
||||||
},
|
RHS: true,
|
||||||
}},
|
},
|
||||||
Echo: AFTER,
|
}},
|
||||||
|
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 {
|
func (this MultExpression) String() string {
|
||||||
m := make([]string, len(this.Expr))
|
m := make([]string, len(this.Expr))
|
||||||
for k := range this.Expr {
|
for k := range this.Expr {
|
||||||
m[k] = print("%v;", this.Expr[k])
|
m[k] = print("%v", this.Expr[k])
|
||||||
}
|
}
|
||||||
return print("(%v)",
|
return print("(%v)",
|
||||||
strings.Join(m, " "),
|
strings.Join(m, "; "),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue