@@ -52,7 +52,8 @@ import (
52
52
// remaining in the parent filter. Much of the format conversions focus
53
53
// on maintaining this invariant.
54
54
func costedIndexScans (ctx * sql.Context , a * Analyzer , n sql.Node , qFlags * sql.QueryFlags ) (sql.Node , transform.TreeIdentity , error ) {
55
- return transform .Node (n , func (n sql.Node ) (sql.Node , transform.TreeIdentity , error ) {
55
+ return transform .NodeWithCtx (n , nil , func (c transform.Context ) (sql.Node , transform.TreeIdentity , error ) {
56
+ n := c .Node
56
57
filter , ok := n .(* plan.Filter )
57
58
if ! ok {
58
59
return n , transform .SameTree , nil
@@ -77,7 +78,7 @@ func costedIndexScans(ctx *sql.Context, a *Analyzer, n sql.Node, qFlags *sql.Que
77
78
return n , transform .SameTree , err
78
79
}
79
80
if ok {
80
- return indexSearchableLookup (ctx , n , rt , lookup , filter .Expression , newFilter , lookupFds , qFlags )
81
+ return indexSearchableLookup (ctx , c , rt , lookup , filter .Expression , newFilter , lookupFds , qFlags )
81
82
} else if is .SkipIndexCosting () {
82
83
return n , transform .SameTree , nil
83
84
}
@@ -89,7 +90,8 @@ func costedIndexScans(ctx *sql.Context, a *Analyzer, n sql.Node, qFlags *sql.Que
89
90
})
90
91
}
91
92
92
- func indexSearchableLookup (ctx * sql.Context , n sql.Node , rt sql.TableNode , lookup sql.IndexLookup , oldFilter , newFilter sql.Expression , fds * sql.FuncDepSet , qFlags * sql.QueryFlags ) (sql.Node , transform.TreeIdentity , error ) {
93
+ func indexSearchableLookup (ctx * sql.Context , c transform.Context , rt sql.TableNode , lookup sql.IndexLookup , oldFilter , newFilter sql.Expression , fds * sql.FuncDepSet , qFlags * sql.QueryFlags ) (sql.Node , transform.TreeIdentity , error ) {
94
+ n := c .Node
93
95
if lookup .IsEmpty () {
94
96
return n , transform .SameTree , nil
95
97
}
@@ -115,9 +117,18 @@ func indexSearchableLookup(ctx *sql.Context, n sql.Node, rt sql.TableNode, looku
115
117
}
116
118
117
119
if fds != nil && fds .HasMax1Row () && ! qFlags .JoinIsSet () && ! qFlags .SubqueryIsSet () && lookup .Ranges .Len () == 1 {
118
- // Strict index lookup without a join or subquery scope will return
119
- // at most one row. We could also use some sort of scope counting
120
- // to check for single scope.
120
+ // Prevent max1Row when parent will combine results
121
+ if c .Parent != nil {
122
+ switch parent := c .Parent .(type ) {
123
+ case * plan.SetOp :
124
+ return ret , transform .NewTree , nil
125
+ case * plan.Project :
126
+ if plan .IsUnary (parent ) {
127
+ return ret , transform .NewTree , nil
128
+ }
129
+ }
130
+ }
131
+
121
132
qFlags .Set (sql .QFlagMax1Row )
122
133
}
123
134
@@ -321,9 +332,6 @@ func getCostedIndexScan(ctx *sql.Context, statsProv sql.StatsProvider, rt sql.Ta
321
332
}
322
333
323
334
if bestStat .FuncDeps ().HasMax1Row () && ! qFlags .JoinIsSet () && ! qFlags .SubqueryIsSet () && lookup .Ranges .Len () == 1 {
324
- // Strict index lookup without a join or subquery scope will return
325
- // at most one row. We could also use some sort of scope counting
326
- // to check for single scope.
327
335
qFlags .Set (sql .QFlagMax1Row )
328
336
}
329
337
0 commit comments