Skip to content

Commit 2673132

Browse files
authored
Merge pull request #1879 from dolthub/jennifer/functions
nested iter should be used for set returning functions as projections only
2 parents 5dba3c9 + 5fd2417 commit 2673132

File tree

4 files changed

+80
-13
lines changed

4 files changed

+80
-13
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ require (
66
github.com/PuerkitoBio/goquery v1.8.1
77
github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a
88
github.com/cockroachdb/errors v1.7.5
9-
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385
9+
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce
1010
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca
1111
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
12-
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c
12+
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f
1313
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216
1414
github.com/dolthub/vitess v0.0.0-20250918181259-ed0e1c5cb192
1515
github.com/fatih/color v1.13.0

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:I
248248
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo=
249249
github.com/dolthub/dolt-mcp v0.2.2-0.20250917171427-13e4520d1c36 h1:1OJhO12aBkZG7WQzAbu1WKCLSPT4AeoSEYeRwCirIi4=
250250
github.com/dolthub/dolt-mcp v0.2.2-0.20250917171427-13e4520d1c36/go.mod h1:S8OtLV2B//aelmZlGnFO44q4YH+Tyfl7jQ9djb4gPJE=
251-
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385 h1:DW4Ey15+x6hPXxooNXsv3jj2h4rOiNxJu2eutlB3XmU=
252-
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385/go.mod h1:6n7es9VAbZghLYPVgetk1cCQ/6UKqQm4oxLgNBu9wiw=
251+
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce h1:vNwEj5mO7F0E4Nc4tswqBrcSS0qU4t7XB8somEyI3vE=
252+
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce/go.mod h1:eVqjIlZw1EOBN5iJJK3Y5s+CbTFF0X7YOIIHt3aqLEc=
253253
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca h1:BGFz/0OlKIuC6qHIZQbvPapFvdAJkeEyGXWVgL5clmE=
254254
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca/go.mod h1:CoDLfgPqHyBtth0Cp+fi/CmC4R81zJNX4wPjShdZ+Bw=
255255
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww=
@@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
258258
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
259259
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc=
260260
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
261-
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c h1:0vvuSeatbCpxh7NgCti9mhZTDCgrgKwIh9YP39EgEBU=
262-
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c/go.mod h1:vYpsrVFnvdhCn3bD7Jsy7lcZsknq9HrqfNpi/DZOg18=
261+
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f h1:Ze7ef+cVv4W62RsaSe55J+9++IT4BkwvFz6kAK/CXwY=
262+
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f/go.mod h1:vYpsrVFnvdhCn3bD7Jsy7lcZsknq9HrqfNpi/DZOg18=
263263
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
264264
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
265265
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE=

server/analyzer/optimize_functions.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package analyzer
1616

1717
import (
18+
"github.com/cockroachdb/errors"
1819
"github.com/dolthub/go-mysql-server/sql"
1920
"github.com/dolthub/go-mysql-server/sql/analyzer"
2021
"github.com/dolthub/go-mysql-server/sql/plan"
@@ -25,37 +26,74 @@ import (
2526
)
2627

2728
// OptimizeFunctions replaces all functions that fit specific criteria with their optimized variants. Also handles
28-
// SRFs (set-returning functions) by setting the `IncludesNestedIters` flag on the Project node if any SRF is found.
29+
// SRFs (set-returning functions) by setting the `IncludesNestedIters` flag on the Project node if any SRF is found
30+
// inside projection expressions.
2931
func OptimizeFunctions(ctx *sql.Context, a *analyzer.Analyzer, node sql.Node, scope *plan.Scope, selector analyzer.RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
3032
// This is supposed to be one of the last rules to run. Subqueries break that assumption, so we skip this rule in such cases.
3133
if scope != nil && scope.CurrentNodeIsFromSubqueryExpression {
3234
return node, transform.SameTree, nil
3335
}
3436

37+
_, isInsertNode := node.(*plan.InsertInto)
3538
return pgtransform.NodeWithOpaque(node, func(n sql.Node) (sql.Node, transform.TreeIdentity, error) {
36-
_, ok := n.(*plan.Project)
39+
projectNode, ok := n.(*plan.Project)
3740
if !ok {
3841
return n, transform.SameTree, nil
3942
}
4043

44+
hasMultipleExpressionTuples := false
4145
hasSRF := false
42-
n, same, err := transform.NodeExprs(n, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
46+
// Check if there is set returning function in the source node (e.g. SELECT * FROM unnest())
47+
n, sameNode, err := transform.NodeExprsWithNode(projectNode.Child, func(in sql.Node, expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
4348
if compiledFunction, ok := expr.(*framework.CompiledFunction); ok {
4449
hasSRF = hasSRF || compiledFunction.IsSRF()
4550
if quickFunction := compiledFunction.GetQuickFunction(); quickFunction != nil {
4651
return quickFunction, transform.NewTree, nil
4752
}
4853
}
54+
if v, ok := in.(*plan.Values); ok {
55+
hasMultipleExpressionTuples = len(v.ExpressionTuples) > 1
56+
}
4957
return expr, transform.SameTree, nil
5058
})
59+
if err != nil {
60+
return nil, transform.SameTree, err
61+
}
62+
if !sameNode {
63+
projectNode.Child = n
64+
}
65+
66+
// insert node cannot have more than 1 row value if it has set returning function
67+
if isInsertNode && hasMultipleExpressionTuples && hasSRF {
68+
return nil, false, errors.Errorf("set-returning functions are not allowed in VALUES")
69+
}
70+
71+
// Check if there is set returning function in the projection expressions (e.g. SELECT unnest() [FROM table/srf])
72+
hasSRFInProjection := false
73+
exprs, sameExprs, err := transform.Exprs(projectNode.Projections, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
74+
if compiledFunction, ok := expr.(*framework.CompiledFunction); ok {
75+
hasSRFInProjection = hasSRFInProjection || compiledFunction.IsSRF()
76+
if quickFunction := compiledFunction.GetQuickFunction(); quickFunction != nil {
77+
return quickFunction, transform.NewTree, nil
78+
}
79+
}
80+
return expr, transform.SameTree, nil
81+
})
82+
if err != nil {
83+
return nil, transform.SameTree, err
84+
}
85+
if !sameExprs {
86+
projectNode.Projections = exprs
87+
}
5188

52-
if hasSRF {
89+
// nested iter is used for set returning functions in the projections only
90+
if hasSRFInProjection {
5391
// Under some conditions, there will be no quick-function replacement, but changing the Project node to include
5492
// nested iterators is still a change we need to tell the transform functions about.
55-
same = transform.NewTree
56-
n = n.(*plan.Project).WithIncludesNestedIters(true)
93+
sameExprs = transform.NewTree
94+
projectNode = projectNode.WithIncludesNestedIters(true)
5795
}
5896

59-
return n, same, err
97+
return projectNode, sameNode && sameExprs, err
6098
})
6199
}

testing/go/functions_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3945,6 +3945,35 @@ func TestSetReturningFunctions(t *testing.T) {
39453945
},
39463946
},
39473947
},
3948+
{
3949+
Name: "generate_series as table function and projection",
3950+
Assertions: []ScriptTestAssertion{
3951+
{
3952+
Query: `SELECT *, unnest(ARRAY['cat', 'dog', 'bird']) AS animal FROM generate_series(1, 3);`,
3953+
Expected: []sql.Row{{1, "cat"}, {1, "dog"}, {1, "bird"}, {2, "cat"}, {2, "dog"}, {2, "bird"}, {3, "cat"}, {3, "dog"}, {3, "bird"}},
3954+
},
3955+
},
3956+
},
3957+
{
3958+
Name: "insert with set returning function",
3959+
SetUpScript: []string{
3960+
"create table hash_parted (a int, b int, c int);",
3961+
},
3962+
Assertions: []ScriptTestAssertion{
3963+
{
3964+
Query: `insert into hash_parted values(0, generate_series(1,3), generate_series(5,8));`,
3965+
Expected: []sql.Row{},
3966+
},
3967+
{
3968+
Query: `insert into hash_parted values(0, generate_series(11,12), generate_series(51,54)), (1, generate_series(1,3), generate_series(5,8));`,
3969+
ExpectedErr: `set-returning functions are not allowed in VALUES`,
3970+
},
3971+
{
3972+
Query: `select * from hash_parted;`,
3973+
Expected: []sql.Row{{0, 1, 5}, {0, 2, 6}, {0, 3, 7}, {0, nil, 8}},
3974+
},
3975+
},
3976+
},
39483977
},
39493978
)
39503979
}

0 commit comments

Comments
 (0)