Skip to content

Commit b5e2698

Browse files
authored
Merge pull request #3137 from dolthub/fulghum/ord_agg_fix
Bug fix: ordered aggregates in triggers
2 parents 466cdf1 + 2bf1c9d commit b5e2698

File tree

3 files changed

+73
-18
lines changed

3 files changed

+73
-18
lines changed

enginetest/queries/trigger_queries.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ var TriggerTests = []ScriptTest{
3131
"create table b (x int primary key)",
3232
"create trigger trig before insert on a for each row begin set new.j = (select coalesce(max(x),1) from b); update b set x = x + 1; end;",
3333
"insert into b values (1)",
34-
"insert into a values (1,0), (2,0), (3,0)",
3534
},
3635
Assertions: []ScriptTestAssertion{
36+
{
37+
Query: "insert into a values (1,0), (2,0), (3,0)",
38+
Expected: []sql.Row{{types.NewOkResult(3)}},
39+
},
3740
{
3841
Query: "select * from a order by i",
3942
Expected: []sql.Row{
@@ -60,9 +63,12 @@ var TriggerTests = []ScriptTest{
6063
"create table a (i int, j int)",
6164
"create table b (x int)",
6265
"create trigger trig before insert on a for each row begin set new.j = (select count(x) from b); insert into b values (new.i + new.j); end;",
63-
"insert into a values (0,0), (0,0), (0,0)",
6466
},
6567
Assertions: []ScriptTestAssertion{
68+
{
69+
Query: "insert into a values (0,0), (0,0), (0,0)",
70+
Expected: []sql.Row{{types.NewOkResult(3)}},
71+
},
6672
{
6773
Query: "select * from a order by j",
6874
Expected: []sql.Row{
@@ -3820,6 +3826,52 @@ end;
38203826
},
38213827
},
38223828
},
3829+
{
3830+
// https://github.com/dolthub/dolt/issues/9616
3831+
Name: "delete trigger using GROUP_CONCAT()",
3832+
SetUpScript: []string{
3833+
`CREATE TABLE orders (
3834+
id INT AUTO_INCREMENT PRIMARY KEY,
3835+
customer_name VARCHAR(100),
3836+
product VARCHAR(100),
3837+
order_date DATETIME DEFAULT CURRENT_TIMESTAMP
3838+
);`,
3839+
`CREATE TABLE order_audit (
3840+
audit_id INT AUTO_INCREMENT PRIMARY KEY,
3841+
customer_name VARCHAR(100),
3842+
all_products TEXT,
3843+
audit_time DATETIME DEFAULT CURRENT_TIMESTAMP
3844+
);`,
3845+
`CREATE TRIGGER before_order_delete
3846+
BEFORE DELETE ON orders
3847+
FOR EACH ROW
3848+
BEGIN
3849+
DECLARE products TEXT;
3850+
SELECT GROUP_CONCAT(product ORDER BY order_date SEPARATOR ', ')
3851+
INTO products
3852+
FROM orders
3853+
WHERE customer_name = OLD.customer_name
3854+
AND id != OLD.id;
3855+
INSERT INTO order_audit (customer_name, all_products)
3856+
VALUES (OLD.customer_name, products);
3857+
END;`,
3858+
`INSERT INTO orders (customer_name, product) VALUES
3859+
('Alice', 'Book'),
3860+
('Alice', 'Pen'),
3861+
('Bob', 'Notebook');`,
3862+
},
3863+
Assertions: []ScriptTestAssertion{
3864+
{
3865+
// Delete a row to trigger the BEFORE DELETE
3866+
Query: "DELETE FROM orders WHERE customer_name = 'Alice' AND product = 'Pen';",
3867+
Expected: []sql.Row{{types.NewOkResult(1)}},
3868+
},
3869+
{
3870+
Query: "SELECT audit_id, customer_name, all_products FROM order_audit;",
3871+
Expected: []sql.Row{{1, "Alice", "Book"}},
3872+
},
3873+
},
3874+
},
38233875
}
38243876

38253877
var TriggerCreateInSubroutineTests = []ScriptTest{

sql/analyzer/fix_exec_indexes.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ type idxScope struct {
165165

166166
triggerScope bool
167167
insertSourceScope bool
168+
subqueryScope bool
168169
}
169170

170171
func (s *idxScope) inTrigger() bool {
@@ -296,10 +297,13 @@ func (s *idxScope) copy() *idxScope {
296297
copy(idsCopy, s.ids)
297298
}
298299
return &idxScope{
299-
lateralScopes: lateralCopy,
300-
parentScopes: parentCopy,
301-
columns: varsCopy,
302-
ids: idsCopy,
300+
lateralScopes: lateralCopy,
301+
parentScopes: parentCopy,
302+
columns: varsCopy,
303+
ids: idsCopy,
304+
subqueryScope: s.subqueryScope,
305+
triggerScope: s.triggerScope,
306+
insertSourceScope: s.insertSourceScope,
303307
}
304308
}
305309

@@ -568,7 +572,7 @@ func (s *idxScope) visitSelf(n sql.Node) error {
568572
if proj, isProj := n.(*plan.Project); isProj {
569573
switch proj.Child.(type) {
570574
case *plan.GroupBy, *plan.Window:
571-
if s.inTrigger() && s.inInsertSource() {
575+
if s.inTrigger() && !s.subqueryScope {
572576
for _, e := range proj.Expressions() {
573577
s.expressions = append(s.expressions, fixExprToScope(e, s.childScopes...))
574578
}
@@ -581,18 +585,15 @@ func (s *idxScope) visitSelf(n sql.Node) error {
581585
// default nodes can't see lateral join nodes, unless we're in lateral
582586
// join and lateral scopes are promoted to parent status
583587
for _, e := range ne.Expressions() {
584-
// OrderedAggregations are special as they append results to the outer scope row
588+
// OrderedAggregations are special as they append a new field to the outer scope row
585589
// We need to account for this extra column in the rows when assigning indexes
586590
// Example: gms/expression/function/aggregation/group_concat.go:groupConcatBuffer.Update()
587-
if ordAgg, isOrdAgg := e.(sql.OrderedAggregation); isOrdAgg {
588-
selExprs := ordAgg.OutputExpressions()
591+
if _, isOrdAgg := e.(sql.OrderedAggregation); isOrdAgg {
589592
selScope := &idxScope{}
590-
for _, expr := range selExprs {
591-
selScope.columns = append(selScope.columns, expr.String())
592-
if gf, isGf := expr.(*expression.GetField); isGf {
593-
selScope.ids = append(selScope.ids, gf.Id())
594-
}
593+
if idExpr, isIdExpr := e.(sql.IdExpression); isIdExpr {
594+
selScope.ids = append(selScope.ids, idExpr.Id())
595595
}
596+
selScope.columns = append(selScope.columns, e.String())
596597
scope = append(scope, selScope)
597598
}
598599
s.expressions = append(s.expressions, fixExprToScope(e, scope...))
@@ -620,7 +621,6 @@ func (s *idxScope) finalizeSelf(n sql.Node) (sql.Node, error) {
620621
}
621622

622623
s.ids = columnIdsForNode(n)
623-
624624
s.addSchema(n.Schema())
625625
var err error
626626
if s.children != nil {
@@ -752,14 +752,17 @@ func fixExprToScope(e sql.Expression, scopes ...*idxScope) sql.Expression {
752752
// this error for the case of DEFAULT in a `plan.Values`, since we analyze the insert source in isolation (we
753753
// don't have the destination schema, and column references in default values are determined in the build phase)
754754

755+
// TODO: If we don't find a valid index for a field, we should report an error
755756
idx, _ := newScope.getIdxId(e.Id(), e.String())
756757
if idx >= 0 {
757758
return e.WithIndex(idx), transform.NewTree, nil
758759
}
759760
return e, transform.SameTree, nil
760761
case *plan.Subquery:
761762
// this |outScope| prepends the subquery scope
762-
newQ, _, err := assignIndexesHelper(e.Query, newScope.push())
763+
subqueryScope := newScope.push()
764+
subqueryScope.subqueryScope = true
765+
newQ, _, err := assignIndexesHelper(e.Query, subqueryScope)
763766
if err != nil {
764767
return nil, transform.SameTree, err
765768
}

sql/expression/function/aggregation/group_concat.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ func (g *groupConcatBuffer) Update(ctx *sql.Context, originalRow sql.Row) error
306306
}
307307
}
308308

309-
// Append the current value to the end of the row. We want to preserve the row's original structure for
309+
// Append the current value to the end of the row. We want to preserve the row's original structure
310310
// for sort ordering in the final step.
311311
g.rows = append(g.rows, append(originalRow, vs))
312312

0 commit comments

Comments
 (0)