forked from vitessio/vitess
/
expr.go
201 lines (191 loc) · 6.36 KB
/
expr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Copyright 2016, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package planbuilder
import (
"bytes"
"errors"
"fmt"
"strconv"
"github.com/youtube/vitess/go/vt/sqlparser"
"github.com/youtube/vitess/go/vt/vtgate/engine"
)
// splitAndExpression breaks up the BoolExpr into AND-separated conditions
// and appends them to filters, which can be shuffled and recombined
// as needed.
func splitAndExpression(filters []sqlparser.BoolExpr, node sqlparser.BoolExpr) []sqlparser.BoolExpr {
if node == nil {
return filters
}
if node, ok := node.(*sqlparser.AndExpr); ok {
filters = splitAndExpression(filters, node.Left)
return splitAndExpression(filters, node.Right)
}
return append(filters, node)
}
// findRoute identifies the right-most route for expr. In situations where
// the expression addresses multiple routes, the expectation is that the
// executor will use the results of the previous routes to feed the necessary
// values for the external references.
// If the expression contains a subquery, the right-most route identification
// also follows the same rules of a normal expression. This is achieved by
// looking at the Externs field of its symbol table that contains the list of
// external references.
// Once the target route is identified, we have to verify that the subquery's
// route can be merged with it. If it cannot, we fail the query. This is because
// we don't have the ability to wire up subqueries through expression evaluation
// primitives. Consequently, if the plan for a subquery comes out as a Join,
// we can immediately error out.
// Since findRoute can itself be called from within a subquery, it has to assume
// that some of the external references may actually be pointing to an outer
// query. The isLocal response from the symtab is used to make sure that we
// only analyze symbols that point to the current symtab.
// If an expression has no references to the current query, then the left-most
// route is chosen as the default.
func findRoute(expr sqlparser.Expr, bldr builder) (rb *route, err error) {
highestRoute := bldr.Leftmost()
var subroutes []*route
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node := node.(type) {
case *sqlparser.ColName:
newRoute, isLocal, err := bldr.Symtab().Find(node, true)
if err != nil {
return false, err
}
if isLocal && newRoute.Order() > highestRoute.Order() {
highestRoute = newRoute
}
case *sqlparser.Subquery:
sel, ok := node.Select.(*sqlparser.Select)
if !ok {
return false, errors.New("unsupported: union operator in subqueries")
}
subplan, err := processSelect(sel, bldr.Symtab().VSchema, bldr)
if err != nil {
return false, err
}
subroute, ok := subplan.(*route)
if !ok {
return false, errors.New("unsupported: complex join in subqueries")
}
for _, extern := range subroute.Symtab().Externs {
// No error expected. These are resolved externs.
newRoute, isLocal, _ := bldr.Symtab().Find(extern, false)
if isLocal && newRoute.Order() > highestRoute.Order() {
highestRoute = newRoute
}
}
subroutes = append(subroutes, subroute)
return false, nil
}
return true, nil
}, expr)
if err != nil {
return nil, err
}
for _, subroute := range subroutes {
err = subqueryCanMerge(highestRoute, subroute)
if err != nil {
return nil, err
}
// This should be moved out if we become capable of processing
// subqueries without push-down.
subroute.Redirect = highestRoute
}
return highestRoute, nil
}
// subqueryCanMerge returns nil if the inner subquery
// can be merged with the specified outer route. If it
// cannot, then it returns an appropriate error.
func subqueryCanMerge(outer, inner *route) error {
if outer.ERoute.Keyspace.Name != inner.ERoute.Keyspace.Name {
return errors.New("unsupported: subquery keyspace different from outer query")
}
if !inner.IsSingle() {
return errors.New("unsupported: scatter subquery")
}
if inner.ERoute.Opcode == engine.SelectUnsharded {
return nil
}
// SelectEqualUnique
switch vals := inner.ERoute.Values.(type) {
case *sqlparser.ColName:
outerVindex := outer.Symtab().Vindex(vals, outer, false)
if outerVindex == inner.ERoute.Vindex {
return nil
}
}
if outer.ERoute.Opcode != engine.SelectEqualUnique {
return errors.New("unsupported: subquery does not depend on scatter outer query")
}
if !valEqual(outer.ERoute.Values, inner.ERoute.Values) {
return errors.New("unsupported: subquery and parent route to different shards")
}
return nil
}
func hasSubquery(node sqlparser.SQLNode) bool {
has := false
_ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
if _, ok := node.(*sqlparser.Subquery); ok {
has = true
return false, errors.New("dummy")
}
return true, nil
}, node)
return has
}
// exprIsValue returns true if the expression can be treated as a value
// for the current route. External references are treated as value.
func exprIsValue(expr sqlparser.ValExpr, rb *route) bool {
switch node := expr.(type) {
case *sqlparser.ColName:
return node.Metadata.(sym).Route() != rb
case sqlparser.ValArg, sqlparser.StrVal, sqlparser.NumVal:
return true
}
return false
}
func valEqual(a, b interface{}) bool {
switch a := a.(type) {
case *sqlparser.ColName:
if b, ok := b.(*sqlparser.ColName); ok {
return newColref(a) == newColref(b)
}
case sqlparser.ValArg:
if b, ok := b.(sqlparser.ValArg); ok {
return bytes.Equal([]byte(a), []byte(b))
}
case sqlparser.StrVal:
if b, ok := b.(sqlparser.StrVal); ok {
return bytes.Equal([]byte(a), []byte(b))
}
case sqlparser.NumVal:
if b, ok := b.(sqlparser.NumVal); ok {
return bytes.Equal([]byte(a), []byte(b))
}
}
return false
}
// valConvert converts an AST value to the Value field in the route.
func valConvert(node sqlparser.ValExpr) (interface{}, error) {
switch node := node.(type) {
case sqlparser.ValArg:
return string(node), nil
case sqlparser.StrVal:
return []byte(node), nil
case sqlparser.NumVal:
val := string(node)
signed, err := strconv.ParseInt(val, 0, 64)
if err == nil {
return signed, nil
}
unsigned, err := strconv.ParseUint(val, 0, 64)
if err == nil {
return unsigned, nil
}
return nil, err
case *sqlparser.NullVal:
return nil, nil
}
return nil, fmt.Errorf("%v is not a value", sqlparser.String(node))
}