Skip to content

Commit fd467d5

Browse files
author
Rafael Chacon
committed
Adds support to scattered updates
Signed-off-by: Rafael Chacon <rafael@slack-corp.com>
1 parent e30c1e1 commit fd467d5

File tree

6 files changed

+185
-35
lines changed

6 files changed

+185
-35
lines changed

data/test/vtgate/dml_cases.txt

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,117 @@
12871287
}
12881288
}
12891289

1290+
# update with no where clause
1291+
"update user_extra set val = 1"
1292+
{
1293+
"Original": "update user_extra set val = 1",
1294+
"Instructions": {
1295+
"Opcode": "UpdateScatter",
1296+
"Keyspace": {
1297+
"Name": "user",
1298+
"Sharded": true
1299+
},
1300+
"Query": "update user_extra set val = 1",
1301+
"Table": "user_extra"
1302+
}
1303+
}
1304+
1305+
1306+
# update with non-comparison expr
1307+
"update user_extra set val = 1 where id between 1 and 2"
1308+
{
1309+
"Original": "update user_extra set val = 1 where id between 1 and 2",
1310+
"Instructions": {
1311+
"Opcode": "UpdateScatter",
1312+
"Keyspace": {
1313+
"Name": "user",
1314+
"Sharded": true
1315+
},
1316+
"Query": "update user_extra set val = 1 where id between 1 and 2",
1317+
"Table": "user_extra"
1318+
}
1319+
}
1320+
1321+
# update with primary id through IN clause
1322+
"update user_extra set val = 1 where user_id in (1, 2)"
1323+
{
1324+
"Original": "update user_extra set val = 1 where user_id in (1, 2)",
1325+
"Instructions": {
1326+
"Opcode": "UpdateScatter",
1327+
"Keyspace": {
1328+
"Name": "user",
1329+
"Sharded": true
1330+
},
1331+
"Query": "update user_extra set val = 1 where user_id in (1, 2)",
1332+
"Table": "user_extra"
1333+
}
1334+
}
1335+
1336+
1337+
# update with non-unique key
1338+
"update user_extra set val = 1 where name = 'foo'"
1339+
{
1340+
"Original": "update user_extra set val = 1 where name = 'foo'",
1341+
"Instructions": {
1342+
"Opcode": "UpdateScatter",
1343+
"Keyspace": {
1344+
"Name": "user",
1345+
"Sharded": true
1346+
},
1347+
"Query": "update user_extra set val = 1 where name = 'foo'",
1348+
"Table": "user_extra"
1349+
}
1350+
}
1351+
1352+
# update with no index match
1353+
"update user_extra set val = 1 where user_id = 1"
1354+
{
1355+
"Original": "update user_extra set val = 1 where user_id = 1",
1356+
"Instructions": {
1357+
"Opcode": "UpdateEqual",
1358+
"Keyspace": {
1359+
"Name": "user",
1360+
"Sharded": true
1361+
},
1362+
"Query": "update user_extra set val = 1 where user_id = 1",
1363+
"Vindex": "user_index",
1364+
"Values": [
1365+
1
1366+
],
1367+
"Table": "user_extra"
1368+
}
1369+
}
1370+
1371+
# update by lookup with IN clause
1372+
"update user_extra set val = 1 where id in (1, 2)"
1373+
{
1374+
"Original": "update user_extra set val = 1 where id in (1, 2)",
1375+
"Instructions": {
1376+
"Opcode": "UpdateScatter",
1377+
"Keyspace": {
1378+
"Name": "user",
1379+
"Sharded": true
1380+
},
1381+
"Query": "update user_extra set val = 1 where id in (1, 2)",
1382+
"Table": "user_extra"
1383+
}
1384+
}
1385+
1386+
# update with where clause with parens
1387+
"update user_extra set val = 1 where (name = 'foo' or id = 1)"
1388+
{
1389+
"Original": "update user_extra set val = 1 where (name = 'foo' or id = 1)",
1390+
"Instructions": {
1391+
"Opcode": "UpdateScatter",
1392+
"Keyspace": {
1393+
"Name": "user",
1394+
"Sharded": true
1395+
},
1396+
"Query": "update user_extra set val = 1 where (name = 'foo' or id = 1)",
1397+
"Table": "user_extra"
1398+
}
1399+
}
1400+
12901401
# delete from with no where clause
12911402
"delete from user_extra"
12921403
{

data/test/vtgate/unsupported_cases.txt

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -291,33 +291,9 @@
291291
"delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)"
292292
"unsupported: sharded subqueries in DML"
293293

294-
# update with no where clause
295-
"update user set val = 1"
296-
"unsupported: multi-shard where clause in DML"
297-
298-
# update with non-comparison expr
299-
"update user set val = 1 where id between 1 and 2"
300-
"unsupported: multi-shard where clause in DML"
301-
302-
# update with primary id through IN clause
303-
"update user set val = 1 where id in (1, 2)"
304-
"unsupported: multi-shard where clause in DML"
305-
306-
# update with non-unique key
307-
"update user set val = 1 where name = 'foo'"
308-
"unsupported: multi-shard where clause in DML"
309-
310-
# update with no index match
311-
"update user set val = 1 where user_id = 1"
312-
"unsupported: multi-shard where clause in DML"
313-
314-
# update by lookup with IN clause
315-
"update music set val = 1 where id in (1, 2)"
316-
"unsupported: multi-shard where clause in DML"
317-
318-
# update with where clause with parens
319-
"update user set val = 1 where (name = 'foo' or id = 1)"
320-
"unsupported: multi-shard where clause in DML"
294+
# scatter update with limit clause
295+
"update user_extra set val = 1 where (name = 'foo' or id = 1) limit 1"
296+
"unsupported: multi shard update with limit"
321297

322298
# delete with multi-table targets
323299
"delete music from music where id = 1"
@@ -327,7 +303,7 @@
327303
"delete user from user join user_extra on user.id = user_extra.id where user.name = 'foo'"
328304
"unsupported: multi-table delete statement in sharded keyspace"
329305

330-
# delete from table with no where clause and owned lookup vindex
306+
# scatter delete with owned lookup vindex
331307
"delete from user"
332308
"unsupported: multi shard delete on a table with owned lookup vindexes"
333309

@@ -504,7 +480,3 @@
504480

505481
"select func(keyspace_id) from user_index where id = :id"
506482
"unsupported: expression on results of a vindex function"
507-
508-
# update with unsupported multi-shard where clause with parens
509-
"update user set val = 1 where (name = 'foo' or id = 1)"
510-
"unsupported: multi-shard where clause in DML"

go/vt/vtgate/engine/update.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,15 @@ const (
106106
// to a single shard: Requires: A Vindex, and
107107
// a single Value.
108108
UpdateEqual
109+
// UpdateScatter is for routing a scattered
110+
// update statement.
111+
UpdateScatter
109112
)
110113

111114
var updName = map[UpdateOpcode]string{
112115
UpdateUnsharded: "UpdateUnsharded",
113116
UpdateEqual: "UpdateEqual",
117+
UpdateScatter: "UpdateScatter",
114118
}
115119

116120
// MarshalJSON serializes the UpdateOpcode as a JSON string.
@@ -126,6 +130,8 @@ func (upd *Update) Execute(vcursor VCursor, bindVars map[string]*querypb.BindVar
126130
return upd.execUpdateUnsharded(vcursor, bindVars)
127131
case UpdateEqual:
128132
return upd.execUpdateEqual(vcursor, bindVars)
133+
case UpdateScatter:
134+
return upd.execUpdateByDestination(vcursor, bindVars, key.DestinationAllShards{})
129135
default:
130136
// Unreachable.
131137
return nil, fmt.Errorf("unsupported opcode: %v", upd)
@@ -218,3 +224,21 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, query string, bindVars m
218224
}
219225
return nil
220226
}
227+
228+
func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string]*querypb.BindVariable, dest key.Destination) (*sqltypes.Result, error) {
229+
rss, _, err := vcursor.ResolveDestinations(upd.Keyspace.Name, nil, []key.Destination{dest})
230+
if err != nil {
231+
return nil, vterrors.Wrap(err, "execDeleteScatter")
232+
}
233+
234+
queries := make([]*querypb.BoundQuery, len(rss))
235+
sql := sqlannotation.AnnotateIfDML(upd.Query, nil)
236+
for i := range rss {
237+
queries[i] = &querypb.BoundQuery{
238+
Sql: sql,
239+
BindVariables: bindVars,
240+
}
241+
}
242+
autocommit := (len(rss) == 1) && vcursor.AutocommitApproval()
243+
return vcursor.ExecuteMultiShard(rss, queries, true /* isDML */, autocommit)
244+
}

go/vt/vtgate/executor_dml_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,25 @@ func TestDeleteEqual(t *testing.T) {
464464
}
465465
}
466466

467+
func TestUpdateScatter(t *testing.T) {
468+
executor, sbc1, sbc2, _ := createExecutorEnv()
469+
_, err := executorExec(executor, "update user_extra set col = 2", nil)
470+
if err != nil {
471+
t.Error(err)
472+
}
473+
// Queries get annotatted.
474+
wantQueries := []*querypb.BoundQuery{{
475+
Sql: "update user_extra set col = 2/* vtgate:: filtered_replication_unfriendly */",
476+
BindVariables: map[string]*querypb.BindVariable{},
477+
}}
478+
if !reflect.DeepEqual(sbc1.Queries, wantQueries) {
479+
t.Errorf("sbc.Queries:\n%+v, want\n%+v\n", sbc1.Queries, wantQueries)
480+
}
481+
if !reflect.DeepEqual(sbc2.Queries, wantQueries) {
482+
t.Errorf("sbc.Queries:\n%+v, want\n%+v\n", sbc2.Queries, wantQueries)
483+
}
484+
}
485+
467486
func TestDeleteScatter(t *testing.T) {
468487
executor, sbc1, sbc2, _ := createExecutorEnv()
469488
_, err := executorExec(executor, "delete from user_extra", nil)

go/vt/vtgate/planbuilder/update.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,19 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd
7373
var err error
7474
eupd.Vindex, eupd.Values, err = getDMLRouting(upd.Where, eupd.Table)
7575
if err != nil {
76-
return nil, err
76+
eupd.Opcode = engine.UpdateScatter
77+
} else {
78+
eupd.Opcode = engine.UpdateEqual
79+
}
80+
81+
if eupd.Opcode == engine.UpdateScatter {
82+
if len(eupd.Table.Owned) != 0 {
83+
return eupd, errors.New("unsupported: multi shard update on a table with owned lookup vindexes")
84+
}
85+
if upd.Limit != nil {
86+
return eupd, errors.New("unsupported: multi shard update with limit")
87+
}
7788
}
78-
eupd.Opcode = engine.UpdateEqual
7989

8090
if eupd.ChangedVindexValues, err = buildChangedVindexesValues(eupd, upd, eupd.Table.ColumnVindexes); err != nil {
8191
return nil, err

test/vtgatev3_test.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ def test_user2(self):
894894
{'id': 3})
895895
vtgate_conn.commit()
896896

897-
# Test multi shard delete
897+
# Test scatter delete
898898
vtgate_conn.begin()
899899
result = self.execute_on_master(
900900
vtgate_conn,
@@ -908,6 +908,20 @@ def test_user2(self):
908908
self.assertEqual(result, ([], 2L, 0L, []))
909909
vtgate_conn.commit()
910910

911+
# Test scatter update
912+
vtgate_conn.begin()
913+
result = self.execute_on_master(
914+
vtgate_conn,
915+
'insert into vt_user (id, name) values (:id0, :name0),(:id1, :name1)',
916+
{'id0': 22, 'name0': 'name2', 'id1': 33, 'name1': 'name2'})
917+
self.assertEqual(result, ([], 2L, 0L, []))
918+
result = self.execute_on_master(
919+
vtgate_conn,
920+
'update vt_user set name=:name where id > :id',
921+
{'id': 20, 'name': 'jose'})
922+
self.assertEqual(result, ([], 2L, 0L, []))
923+
vtgate_conn.commit()
924+
911925
def test_user_truncate(self):
912926
vtgate_conn = get_connection()
913927
vtgate_conn.begin()

0 commit comments

Comments
 (0)