diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index c409e3ac7c..77c524da65 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -122,6 +122,30 @@ type ScriptTestAssertion struct { // Unlike other engine tests, ScriptTests must be self-contained. No other tables are created outside the definition of // the tests. var ScriptTests = []ScriptTest{ + { + // https://github.com/dolthub/dolt/issues/9987 + Name: "GROUP BY nil pointer dereference in Dispose when Next() never called", + SetUpScript: []string{ + "CREATE TABLE test_table (id INT PRIMARY KEY, value INT, category VARCHAR(50))", + "INSERT INTO test_table VALUES (1, 100, 'A'), (2, 200, 'B'), (3, 300, 'A')", + }, + Assertions: []ScriptTestAssertion{ + { + // LIMIT 0 causes the iterator to close without ever calling Next() on groupByIter + // This leaves all buffer elements as nil causing panic in Dispose(), or empty depending data struct + Query: "SELECT category, SUM(value) FROM test_table GROUP BY category LIMIT 0", + Expected: []sql.Row{}, + }, + { + Query: "SELECT category, COUNT(*) FROM test_table GROUP BY category LIMIT 0", + Expected: []sql.Row{}, + }, + { + Query: "SELECT SUM(value) FROM test_table LIMIT 0", + Expected: []sql.Row{}, + }, + }, + }, { // https://github.com/dolthub/dolt/issues/9935 Dialect: "mysql", diff --git a/sql/rowexec/agg.go b/sql/rowexec/agg.go index e529432caf..0f72120641 100644 --- a/sql/rowexec/agg.go +++ b/sql/rowexec/agg.go @@ -103,7 +103,9 @@ func (i *groupByIter) Close(ctx *sql.Context) error { func (i *groupByIter) Dispose() { for _, b := range i.buf { - b.Dispose() + if b != nil { + b.Dispose() + } } }