Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a
github.com/cockroachdb/errors v1.7.5
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216
github.com/dolthub/vitess v0.0.0-20250918181259-ed0e1c5cb192
github.com/fatih/color v1.13.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:I
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo=
github.com/dolthub/dolt-mcp v0.2.2-0.20250917171427-13e4520d1c36 h1:1OJhO12aBkZG7WQzAbu1WKCLSPT4AeoSEYeRwCirIi4=
github.com/dolthub/dolt-mcp v0.2.2-0.20250917171427-13e4520d1c36/go.mod h1:S8OtLV2B//aelmZlGnFO44q4YH+Tyfl7jQ9djb4gPJE=
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385 h1:DW4Ey15+x6hPXxooNXsv3jj2h4rOiNxJu2eutlB3XmU=
github.com/dolthub/dolt/go v0.40.5-0.20250919235907-2afe4e108385/go.mod h1:6n7es9VAbZghLYPVgetk1cCQ/6UKqQm4oxLgNBu9wiw=
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce h1:vNwEj5mO7F0E4Nc4tswqBrcSS0qU4t7XB8somEyI3vE=
github.com/dolthub/dolt/go v0.40.5-0.20250923175150-9d9f7d8c96ce/go.mod h1:eVqjIlZw1EOBN5iJJK3Y5s+CbTFF0X7YOIIHt3aqLEc=
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca h1:BGFz/0OlKIuC6qHIZQbvPapFvdAJkeEyGXWVgL5clmE=
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca/go.mod h1:CoDLfgPqHyBtth0Cp+fi/CmC4R81zJNX4wPjShdZ+Bw=
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww=
Expand All @@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c h1:0vvuSeatbCpxh7NgCti9mhZTDCgrgKwIh9YP39EgEBU=
github.com/dolthub/go-mysql-server v0.20.1-0.20250919010705-b0670cfce79c/go.mod h1:vYpsrVFnvdhCn3bD7Jsy7lcZsknq9HrqfNpi/DZOg18=
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f h1:Ze7ef+cVv4W62RsaSe55J+9++IT4BkwvFz6kAK/CXwY=
github.com/dolthub/go-mysql-server v0.20.1-0.20250923164830-5d3eaa8e1a9f/go.mod h1:vYpsrVFnvdhCn3bD7Jsy7lcZsknq9HrqfNpi/DZOg18=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE=
Expand Down
52 changes: 45 additions & 7 deletions server/analyzer/optimize_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package analyzer

import (
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/analyzer"
"github.com/dolthub/go-mysql-server/sql/plan"
Expand All @@ -25,37 +26,74 @@ import (
)

// OptimizeFunctions replaces all functions that fit specific criteria with their optimized variants. Also handles
// SRFs (set-returning functions) by setting the `IncludesNestedIters` flag on the Project node if any SRF is found.
// SRFs (set-returning functions) by setting the `IncludesNestedIters` flag on the Project node if any SRF is found
// inside projection expressions.
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) {
// This is supposed to be one of the last rules to run. Subqueries break that assumption, so we skip this rule in such cases.
if scope != nil && scope.CurrentNodeIsFromSubqueryExpression {
return node, transform.SameTree, nil
}

_, isInsertNode := node.(*plan.InsertInto)
return pgtransform.NodeWithOpaque(node, func(n sql.Node) (sql.Node, transform.TreeIdentity, error) {
_, ok := n.(*plan.Project)
projectNode, ok := n.(*plan.Project)
if !ok {
return n, transform.SameTree, nil
}

hasMultipleExpressionTuples := false
hasSRF := false
n, same, err := transform.NodeExprs(n, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
// Check if there is set returning function in the source node (e.g. SELECT * FROM unnest())
n, sameNode, err := transform.NodeExprsWithNode(projectNode.Child, func(in sql.Node, expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
if compiledFunction, ok := expr.(*framework.CompiledFunction); ok {
hasSRF = hasSRF || compiledFunction.IsSRF()
if quickFunction := compiledFunction.GetQuickFunction(); quickFunction != nil {
return quickFunction, transform.NewTree, nil
}
}
if v, ok := in.(*plan.Values); ok {
hasMultipleExpressionTuples = len(v.ExpressionTuples) > 1
}
return expr, transform.SameTree, nil
})
if err != nil {
return nil, transform.SameTree, err
}
if !sameNode {
projectNode.Child = n
}

// insert node cannot have more than 1 row value if it has set returning function
if isInsertNode && hasMultipleExpressionTuples && hasSRF {
return nil, false, errors.Errorf("set-returning functions are not allowed in VALUES")
}

// Check if there is set returning function in the projection expressions (e.g. SELECT unnest() [FROM table/srf])
hasSRFInProjection := false
exprs, sameExprs, err := transform.Exprs(projectNode.Projections, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
if compiledFunction, ok := expr.(*framework.CompiledFunction); ok {
hasSRFInProjection = hasSRFInProjection || compiledFunction.IsSRF()
if quickFunction := compiledFunction.GetQuickFunction(); quickFunction != nil {
return quickFunction, transform.NewTree, nil
}
}
return expr, transform.SameTree, nil
})
if err != nil {
return nil, transform.SameTree, err
}
if !sameExprs {
projectNode.Projections = exprs
}

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

return n, same, err
return projectNode, sameNode && sameExprs, err
})
}
29 changes: 29 additions & 0 deletions testing/go/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3945,6 +3945,35 @@ func TestSetReturningFunctions(t *testing.T) {
},
},
},
{
Name: "generate_series as table function and projection",
Assertions: []ScriptTestAssertion{
{
Query: `SELECT *, unnest(ARRAY['cat', 'dog', 'bird']) AS animal FROM generate_series(1, 3);`,
Expected: []sql.Row{{1, "cat"}, {1, "dog"}, {1, "bird"}, {2, "cat"}, {2, "dog"}, {2, "bird"}, {3, "cat"}, {3, "dog"}, {3, "bird"}},
},
},
},
{
Name: "insert with set returning function",
SetUpScript: []string{
"create table hash_parted (a int, b int, c int);",
},
Assertions: []ScriptTestAssertion{
{
Query: `insert into hash_parted values(0, generate_series(1,3), generate_series(5,8));`,
Expected: []sql.Row{},
},
{
Query: `insert into hash_parted values(0, generate_series(11,12), generate_series(51,54)), (1, generate_series(1,3), generate_series(5,8));`,
ExpectedErr: `set-returning functions are not allowed in VALUES`,
},
{
Query: `select * from hash_parted;`,
Expected: []sql.Row{{0, 1, 5}, {0, 2, 6}, {0, 3, 7}, {0, nil, 8}},
},
},
},
},
)
}
Loading