Skip to content

Commit b7ef410

Browse files
Fix view tracking on sharded keyspace (#15436)
Signed-off-by: Harshit Gangal <[email protected]> Signed-off-by: Andres Taylor <[email protected]> Co-authored-by: Andres Taylor <[email protected]>
1 parent 09bb2e2 commit b7ef410

File tree

10 files changed

+102
-85
lines changed

10 files changed

+102
-85
lines changed

go/test/endtoend/utils/utils.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,17 +280,23 @@ func WaitForKsError(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string
280280
var ok bool
281281
errString, ok = ksErr.(string)
282282
return ok
283-
})
283+
}, "Waiting for error")
284284
return errString
285285
}
286286

287287
// WaitForVschemaCondition waits for the condition to be true
288-
func WaitForVschemaCondition(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string, conditionMet func(t *testing.T, keyspace map[string]interface{}) bool) {
288+
func WaitForVschemaCondition(
289+
t *testing.T,
290+
vtgateProcess cluster.VtgateProcess,
291+
ks string,
292+
conditionMet func(t *testing.T, keyspace map[string]interface{}) bool,
293+
message string,
294+
) {
289295
timeout := time.After(60 * time.Second)
290296
for {
291297
select {
292298
case <-timeout:
293-
t.Fatalf("schema tracking did not met the condition within the time for keyspace: %s", ks)
299+
t.Fatalf("schema tracking did not met the condition within the time for keyspace: %s\n%s", ks, message)
294300
default:
295301
res, err := vtgateProcess.ReadVSchema()
296302
require.NoError(t, err, res)
@@ -305,12 +311,12 @@ func WaitForVschemaCondition(t *testing.T, vtgateProcess cluster.VtgateProcess,
305311
}
306312

307313
// WaitForTableDeletions waits for a table to be deleted
308-
func WaitForTableDeletions(ctx context.Context, t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl string) {
314+
func WaitForTableDeletions(t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl string) {
309315
WaitForVschemaCondition(t, vtgateProcess, ks, func(t *testing.T, keyspace map[string]interface{}) bool {
310316
tablesMap := keyspace["tables"]
311317
_, isPresent := convertToMap(tablesMap)[tbl]
312318
return !isPresent
313-
})
319+
}, "Waiting for table to be deleted")
314320
}
315321

316322
// WaitForColumn waits for a table's column to be present

go/test/endtoend/vtgate/foreignkey/fk_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,12 +1175,11 @@ func TestCyclicFks(t *testing.T) {
11751175
// Drop the cyclic foreign key constraint.
11761176
utils.Exec(t, mcmp.VtConn, "alter table fk_t10 drop foreign key test_cyclic_fks")
11771177

1178-
// Wait for schema-tracking to be complete.
1179-
utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, unshardedKs, func(t *testing.T, keyspace map[string]interface{}) bool {
1178+
// Let's clean out the cycle so that the other tests don't fail
1179+
utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, unshardedKs, func(t *testing.T, keyspace map[string]any) bool {
11801180
_, fieldPresent := keyspace["error"]
11811181
return !fieldPresent
1182-
})
1183-
1182+
}, "wait for error to disappear")
11841183
}
11851184

11861185
func TestReplace(t *testing.T) {

go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,8 @@ func createInitialSchema(t *testing.T, tcase *testCase) {
786786
}
787787
})
788788
t.Run("waiting for vschema deletions to apply", func(t *testing.T) {
789-
timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Minute)
790-
defer cancel()
791789
for _, tableName := range tableNames {
792-
utils.WaitForTableDeletions(timeoutCtx, t, clusterInstance.VtgateProcess, keyspaceName, tableName)
790+
utils.WaitForTableDeletions(t, clusterInstance.VtgateProcess, keyspaceName, tableName)
793791
}
794792
})
795793
t.Run("creating tables", func(t *testing.T) {

go/test/endtoend/vtgate/misc_test.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -321,19 +321,6 @@ func TestCreateIndex(t *testing.T) {
321321
utils.Exec(t, conn, `create index i2 on ks.t1000 (id1)`)
322322
}
323323

324-
func TestCreateView(t *testing.T) {
325-
// The test wont work since we cant change the vschema without reloading the vtgate.
326-
t.Skip()
327-
conn, closer := start(t)
328-
defer closer()
329-
// Test that create view works and the output is as expected
330-
utils.Exec(t, conn, `create view v1 as select * from t1`)
331-
utils.Exec(t, conn, `insert into t1(id1, id2) values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)`)
332-
// This wont work, since ALTER VSCHEMA ADD TABLE is only supported for unsharded keyspaces
333-
utils.Exec(t, conn, "alter vschema add table v1")
334-
utils.AssertMatches(t, conn, "select * from v1", `[[INT64(1) INT64(1)] [INT64(2) INT64(2)] [INT64(3) INT64(3)] [INT64(4) INT64(4)] [INT64(5) INT64(5)]]`)
335-
}
336-
337324
func TestVersions(t *testing.T) {
338325
conn, closer := start(t)
339326
defer closer()

go/test/endtoend/vtgate/queries/misc/main_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ func TestMain(m *testing.M) {
7373
return 1
7474
}
7575

76+
clusterInstance.VtGateExtraArgs = append(clusterInstance.VtGateExtraArgs, "--enable-views")
77+
clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-enable-views")
78+
7679
// Start keyspace
7780
keyspace := &cluster.Keyspace{
7881
Name: keyspaceName,

go/test/endtoend/vtgate/queries/misc/misc_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"strconv"
2323
"strings"
2424
"testing"
25+
"time"
2526

2627
_ "github.com/go-sql-driver/mysql"
2728
"github.com/stretchr/testify/assert"
@@ -371,3 +372,56 @@ func TestAliasesInOuterJoinQueries(t *testing.T) {
371372
mcmp.AssertMatches("select t1.id1 as t0, t1.id1 as t1, tbl.unq_col as col from t1 left outer join tbl on t1.id2 = tbl.nonunq_col order by t1.id2 limit 2", `[[INT64(1) INT64(1) INT64(42)] [INT64(42) INT64(42) NULL]]`)
372373
mcmp.ExecWithColumnCompare("select t1.id1 as t0, t1.id1 as t1, tbl.unq_col as col from t1 left outer join tbl on t1.id2 = tbl.nonunq_col order by t1.id2 limit 2")
373374
}
375+
376+
func TestAlterTableWithView(t *testing.T) {
377+
utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate")
378+
mcmp, closer := start(t)
379+
defer closer()
380+
381+
// Test that create/alter view works and the output is as expected
382+
mcmp.Exec(`use ks_misc`)
383+
mcmp.Exec(`create view v1 as select * from t1`)
384+
var viewDef string
385+
utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, keyspaceName, func(t *testing.T, ksMap map[string]any) bool {
386+
views, ok := ksMap["views"]
387+
if !ok {
388+
return false
389+
}
390+
viewsMap := views.(map[string]any)
391+
view, ok := viewsMap["v1"]
392+
if ok {
393+
viewDef = view.(string)
394+
}
395+
return ok
396+
}, "Waiting for view creation")
397+
mcmp.Exec(`insert into t1(id1, id2) values (1, 1)`)
398+
mcmp.AssertMatches("select * from v1", `[[INT64(1) INT64(1)]]`)
399+
400+
// alter table add column
401+
mcmp.Exec(`alter table t1 add column test bigint`)
402+
time.Sleep(10 * time.Second)
403+
mcmp.Exec(`alter view v1 as select * from t1`)
404+
405+
waitForChange := func(t *testing.T, ksMap map[string]any) bool {
406+
// wait for the view definition to change
407+
views := ksMap["views"]
408+
viewsMap := views.(map[string]any)
409+
newView := viewsMap["v1"]
410+
if newView.(string) == viewDef {
411+
return false
412+
}
413+
viewDef = newView.(string)
414+
return true
415+
}
416+
utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, keyspaceName, waitForChange, "Waiting for alter view")
417+
418+
mcmp.AssertMatches("select * from v1", `[[INT64(1) INT64(1) NULL]]`)
419+
420+
// alter table remove column
421+
mcmp.Exec(`alter table t1 drop column test`)
422+
mcmp.Exec(`alter view v1 as select * from t1`)
423+
424+
utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, keyspaceName, waitForChange, "Waiting for alter view")
425+
426+
mcmp.AssertMatches("select * from v1", `[[INT64(1) INT64(1)]]`)
427+
}

go/vt/vtgate/planbuilder/ddl.go

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLSt
112112
}
113113
err = checkFKError(vschema, ddlStatement, keyspace)
114114
case *sqlparser.CreateView:
115-
destination, keyspace, err = buildCreateView(ctx, vschema, ddl, reservedVars, enableOnlineDDL, enableDirectDDL)
115+
destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl)
116116
case *sqlparser.AlterView:
117-
destination, keyspace, err = buildAlterView(ctx, vschema, ddl, reservedVars, enableOnlineDDL, enableDirectDDL)
117+
destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, enableOnlineDDL, enableDirectDDL, ddl.Select, ddl)
118118
case *sqlparser.DropView:
119119
destination, keyspace, err = buildDropView(vschema, ddlStatement)
120120
case *sqlparser.DropTable:
@@ -192,64 +192,43 @@ func findTableDestinationAndKeyspace(vschema plancontext.VSchema, ddlStatement s
192192
return destination, keyspace, nil
193193
}
194194

195-
func buildAlterView(ctx context.Context, vschema plancontext.VSchema, ddl *sqlparser.AlterView, reservedVars *sqlparser.ReservedVars, enableOnlineDDL, enableDirectDDL bool) (key.Destination, *vindexes.Keyspace, error) {
196-
// For Alter View, we require that the view exist and the select query can be satisfied within the keyspace itself
195+
func buildCreateViewCommon(
196+
ctx context.Context,
197+
vschema plancontext.VSchema,
198+
reservedVars *sqlparser.ReservedVars,
199+
enableOnlineDDL, enableDirectDDL bool,
200+
ddlSelect sqlparser.SelectStatement,
201+
ddl sqlparser.DDLStatement,
202+
) (key.Destination, *vindexes.Keyspace, error) {
203+
// For Create View, we require that the keyspace exist and the select query can be satisfied within the keyspace itself
197204
// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
198205
destination, keyspace, err := findTableDestinationAndKeyspace(vschema, ddl)
199206
if err != nil {
200207
return nil, nil, err
201208
}
202209

203-
selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddl.Select), ddl.Select, reservedVars, vschema, enableOnlineDDL, enableDirectDDL)
210+
// because we don't trust the schema tracker to have up-to-date info, we don't want to expand any SELECT * here
211+
var expressions []sqlparser.SelectExprs
212+
_ = sqlparser.VisitAllSelects(ddlSelect, func(p *sqlparser.Select, idx int) error {
213+
expressions = append(expressions, sqlparser.CloneSelectExprs(p.SelectExprs))
214+
return nil
215+
})
216+
selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddlSelect), ddlSelect, reservedVars, vschema, enableOnlineDDL, enableDirectDDL)
204217
if err != nil {
205218
return nil, nil, err
206219
}
207220
selPlanKs := selectPlan.primitive.GetKeyspaceName()
208221
if keyspace.Name != selPlanKs {
209222
return nil, nil, vterrors.VT12001(ViewDifferentKeyspace)
210223
}
211-
if vschema.IsViewsEnabled() {
212-
if keyspace == nil {
213-
return nil, nil, vterrors.VT09005()
214-
}
215-
return destination, keyspace, nil
216-
}
217-
isRoutePlan, opCode := tryToGetRoutePlan(selectPlan.primitive)
218-
if !isRoutePlan {
219-
return nil, nil, vterrors.VT12001(ViewComplex)
220-
}
221-
if opCode != engine.Unsharded && opCode != engine.EqualUnique && opCode != engine.Scatter {
222-
return nil, nil, vterrors.VT12001(ViewComplex)
223-
}
224-
_ = sqlparser.SafeRewrite(ddl.Select, nil, func(cursor *sqlparser.Cursor) bool {
225-
switch tableName := cursor.Node().(type) {
226-
case sqlparser.TableName:
227-
cursor.Replace(sqlparser.TableName{
228-
Name: tableName.Name,
229-
})
230-
}
231-
return true
224+
225+
_ = sqlparser.VisitAllSelects(ddlSelect, func(p *sqlparser.Select, idx int) error {
226+
p.SelectExprs = expressions[idx]
227+
return nil
232228
})
233-
return destination, keyspace, nil
234-
}
235229

236-
func buildCreateView(ctx context.Context, vschema plancontext.VSchema, ddl *sqlparser.CreateView, reservedVars *sqlparser.ReservedVars, enableOnlineDDL, enableDirectDDL bool) (key.Destination, *vindexes.Keyspace, error) {
237-
// For Create View, we require that the keyspace exist and the select query can be satisfied within the keyspace itself
238-
// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
239-
destination, keyspace, _, err := vschema.TargetDestination(ddl.ViewName.Qualifier.String())
240-
if err != nil {
241-
return nil, nil, err
242-
}
243-
ddl.ViewName.Qualifier = sqlparser.NewIdentifierCS("")
230+
sqlparser.RemoveKeyspace(ddl)
244231

245-
selectPlan, err := createInstructionFor(ctx, sqlparser.String(ddl.Select), ddl.Select, reservedVars, vschema, enableOnlineDDL, enableDirectDDL)
246-
if err != nil {
247-
return nil, nil, err
248-
}
249-
selPlanKs := selectPlan.primitive.GetKeyspaceName()
250-
if keyspace.Name != selPlanKs {
251-
return nil, nil, vterrors.VT12001(ViewDifferentKeyspace)
252-
}
253232
if vschema.IsViewsEnabled() {
254233
if keyspace == nil {
255234
return nil, nil, vterrors.VT09005()
@@ -263,15 +242,6 @@ func buildCreateView(ctx context.Context, vschema plancontext.VSchema, ddl *sqlp
263242
if opCode != engine.Unsharded && opCode != engine.EqualUnique && opCode != engine.Scatter {
264243
return nil, nil, vterrors.VT12001(ViewComplex)
265244
}
266-
_ = sqlparser.SafeRewrite(ddl.Select, nil, func(cursor *sqlparser.Cursor) bool {
267-
switch tableName := cursor.Node().(type) {
268-
case sqlparser.TableName:
269-
cursor.Replace(sqlparser.TableName{
270-
Name: tableName.Name,
271-
})
272-
}
273-
return true
274-
})
275245
return destination, keyspace, nil
276246
}
277247

go/vt/vtgate/planbuilder/testdata/ddl_cases.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@
374374
"Name": "user",
375375
"Sharded": true
376376
},
377-
"Query": "create view tmp_view as select user_id, col1, col2 from authoritative"
377+
"Query": "create view tmp_view as select * from authoritative"
378378
},
379379
"TablesUsed": [
380380
"user.tmp_view"

go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
"Name": "user",
126126
"Sharded": true
127127
},
128-
"Query": "create view view_a as select user_id, col1, col2 from authoritative"
128+
"Query": "create view view_a as select * from authoritative"
129129
},
130130
"TablesUsed": [
131131
"user.view_a"
@@ -144,7 +144,7 @@
144144
"Name": "user",
145145
"Sharded": true
146146
},
147-
"Query": "create view view_a as select a.user_id, a.col1, a.col2, b.user_id, b.col1, b.col2 from authoritative as a join authoritative as b on a.user_id = b.user_id"
147+
"Query": "create view view_a as select * from authoritative as a join authoritative as b on a.user_id = b.user_id"
148148
},
149149
"TablesUsed": [
150150
"user.view_a"
@@ -163,7 +163,7 @@
163163
"Name": "user",
164164
"Sharded": true
165165
},
166-
"Query": "create view view_a as select user_id, col1, col2 from authoritative as a"
166+
"Query": "create view view_a as select a.* from authoritative as a"
167167
},
168168
"TablesUsed": [
169169
"user.view_a"
@@ -201,7 +201,7 @@
201201
"Name": "user",
202202
"Sharded": true
203203
},
204-
"Query": "create view view_a as select `user`.id, a.user_id, a.col1, a.col2, `user`.col1 from authoritative as a join `user` on a.user_id = `user`.id"
204+
"Query": "create view view_a as select `user`.id, a.*, `user`.col1 from authoritative as a join `user` on a.user_id = `user`.id"
205205
},
206206
"TablesUsed": [
207207
"user.view_a"

go/vt/vtgate/planbuilder/testdata/view_cases.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"Name": "user",
1212
"Sharded": true
1313
},
14-
"Query": "alter view user_extra as select * from `user`.`user`"
14+
"Query": "alter view user_extra as select * from `user`"
1515
},
1616
"TablesUsed": [
1717
"user.user_extra"
@@ -35,7 +35,7 @@
3535
"Name": "user",
3636
"Sharded": true
3737
},
38-
"Query": "create view view_ac as select user_id, col1, col2 from authoritative"
38+
"Query": "create view view_ac as select * from authoritative"
3939
},
4040
"TablesUsed": [
4141
"user.view_ac"

0 commit comments

Comments
 (0)