Skip to content

Commit c8697f8

Browse files
fix: route engine to handle column truncation for execute after lookup (#16981)
Signed-off-by: Harshit Gangal <harshit@planetscale.com>
1 parent b7adf46 commit c8697f8

2 files changed

Lines changed: 146 additions & 20 deletions

File tree

go/vt/vtgate/engine/route.go

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,12 @@ func (route *Route) SetTruncateColumnCount(count int) {
166166
func (route *Route) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
167167
ctx, cancelFunc := addQueryTimeout(ctx, vcursor, route.QueryTimeout)
168168
defer cancelFunc()
169-
qr, err := route.executeInternal(ctx, vcursor, bindVars, wantfields)
169+
rss, bvs, err := route.findRoute(ctx, vcursor, bindVars)
170170
if err != nil {
171171
return nil, err
172172
}
173-
return qr.Truncate(route.TruncateColumnCount), nil
173+
174+
return route.executeShards(ctx, vcursor, bindVars, wantfields, rss, bvs)
174175
}
175176

176177
// addQueryTimeout adds a query timeout to the context it receives and returns the modified context along with the cancel function.
@@ -188,20 +189,6 @@ const (
188189
IgnoreReserveTxn cxtKey = iota
189190
)
190191

191-
func (route *Route) executeInternal(
192-
ctx context.Context,
193-
vcursor VCursor,
194-
bindVars map[string]*querypb.BindVariable,
195-
wantfields bool,
196-
) (*sqltypes.Result, error) {
197-
rss, bvs, err := route.findRoute(ctx, vcursor, bindVars)
198-
if err != nil {
199-
return nil, err
200-
}
201-
202-
return route.executeShards(ctx, vcursor, bindVars, wantfields, rss, bvs)
203-
}
204-
205192
func (route *Route) executeShards(
206193
ctx context.Context,
207194
vcursor VCursor,
@@ -255,11 +242,15 @@ func (route *Route) executeShards(
255242
}
256243
}
257244

258-
if len(route.OrderBy) == 0 {
259-
return result, nil
245+
if len(route.OrderBy) != 0 {
246+
var err error
247+
result, err = route.sort(result)
248+
if err != nil {
249+
return nil, err
250+
}
260251
}
261252

262-
return route.sort(result)
253+
return result.Truncate(route.TruncateColumnCount), nil
263254
}
264255

265256
func filterOutNilErrors(errs []error) []error {
@@ -441,7 +432,7 @@ func (route *Route) sort(in *sqltypes.Result) (*sqltypes.Result, error) {
441432
return 0
442433
})
443434

444-
return out.Truncate(route.TruncateColumnCount), err
435+
return out, err
445436
}
446437

447438
func (route *Route) description() PrimitiveDescription {
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
Copyright 2024 The Vitess Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package engine
18+
19+
import (
20+
"context"
21+
"testing"
22+
23+
"github.com/stretchr/testify/require"
24+
25+
"vitess.io/vitess/go/sqltypes"
26+
querypb "vitess.io/vitess/go/vt/proto/query"
27+
"vitess.io/vitess/go/vt/vtgate/evalengine"
28+
"vitess.io/vitess/go/vt/vtgate/vindexes"
29+
)
30+
31+
var (
32+
vindex, _ = vindexes.CreateVindex("lookup_unique", "", map[string]string{
33+
"table": "lkp",
34+
"from": "from",
35+
"to": "toc",
36+
"write_only": "true",
37+
})
38+
ks = &vindexes.Keyspace{Name: "ks", Sharded: true}
39+
)
40+
41+
func TestVindexLookup(t *testing.T) {
42+
planableVindex, ok := vindex.(vindexes.LookupPlanable)
43+
require.True(t, ok, "not a lookup vindex")
44+
_, args := planableVindex.Query()
45+
46+
fp := &fakePrimitive{
47+
results: []*sqltypes.Result{
48+
sqltypes.MakeTestResult(
49+
sqltypes.MakeTestFields("id|keyspace_id", "int64|varbinary"),
50+
"1|\x10"),
51+
},
52+
}
53+
route := NewRoute(ByDestination, ks, "dummy_select", "dummy_select_field")
54+
vdxLookup := &VindexLookup{
55+
Opcode: EqualUnique,
56+
Keyspace: ks,
57+
Vindex: planableVindex,
58+
Arguments: args,
59+
Values: []evalengine.Expr{evalengine.NewLiteralInt(1)},
60+
Lookup: fp,
61+
SendTo: route,
62+
}
63+
64+
vc := &loggingVCursor{results: []*sqltypes.Result{defaultSelectResult}}
65+
66+
result, err := vdxLookup.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false)
67+
require.NoError(t, err)
68+
fp.ExpectLog(t, []string{`Execute from: type:TUPLE values:{type:INT64 value:"1"} false`})
69+
vc.ExpectLog(t, []string{
70+
`ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(10)`,
71+
`ExecuteMultiShard ks.-20: dummy_select {} false false`,
72+
})
73+
expectResult(t, result, defaultSelectResult)
74+
75+
fp.rewind()
76+
vc.Rewind()
77+
result, err = wrapStreamExecute(vdxLookup, vc, map[string]*querypb.BindVariable{}, false)
78+
require.NoError(t, err)
79+
vc.ExpectLog(t, []string{
80+
`ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(10)`,
81+
`StreamExecuteMulti dummy_select ks.-20: {} `,
82+
})
83+
expectResult(t, result, defaultSelectResult)
84+
}
85+
86+
func TestVindexLookupTruncate(t *testing.T) {
87+
planableVindex, ok := vindex.(vindexes.LookupPlanable)
88+
require.True(t, ok, "not a lookup vindex")
89+
_, args := planableVindex.Query()
90+
91+
fp := &fakePrimitive{
92+
results: []*sqltypes.Result{
93+
sqltypes.MakeTestResult(
94+
sqltypes.MakeTestFields("id|keyspace_id", "int64|varbinary"),
95+
"1|\x10"),
96+
},
97+
}
98+
route := NewRoute(ByDestination, ks, "dummy_select", "dummy_select_field")
99+
route.TruncateColumnCount = 1
100+
vdxLookup := &VindexLookup{
101+
Opcode: EqualUnique,
102+
Keyspace: ks,
103+
Vindex: planableVindex,
104+
Arguments: args,
105+
Values: []evalengine.Expr{evalengine.NewLiteralInt(1)},
106+
Lookup: fp,
107+
SendTo: route,
108+
}
109+
110+
vc := &loggingVCursor{results: []*sqltypes.Result{
111+
sqltypes.MakeTestResult(sqltypes.MakeTestFields("name|morecol", "varchar|int64"),
112+
"foo|1", "bar|2", "baz|3"),
113+
}}
114+
115+
wantRes := sqltypes.MakeTestResult(sqltypes.MakeTestFields("name", "varchar"),
116+
"foo", "bar", "baz")
117+
result, err := vdxLookup.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false)
118+
require.NoError(t, err)
119+
fp.ExpectLog(t, []string{`Execute from: type:TUPLE values:{type:INT64 value:"1"} false`})
120+
vc.ExpectLog(t, []string{
121+
`ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(10)`,
122+
`ExecuteMultiShard ks.-20: dummy_select {} false false`,
123+
})
124+
expectResult(t, result, wantRes)
125+
126+
fp.rewind()
127+
vc.Rewind()
128+
result, err = wrapStreamExecute(vdxLookup, vc, map[string]*querypb.BindVariable{}, false)
129+
require.NoError(t, err)
130+
vc.ExpectLog(t, []string{
131+
`ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(10)`,
132+
`StreamExecuteMulti dummy_select ks.-20: {} `,
133+
})
134+
expectResult(t, result, wantRes)
135+
}

0 commit comments

Comments
 (0)