Skip to content

Commit

Permalink
Merge pull request vitessio#8393 from planetscale/delete-alias
Browse files Browse the repository at this point in the history
Delete table reference alias support
  • Loading branch information
harshit-gangal authored Jun 28, 2021
2 parents 6dd97de + 1fea7f9 commit 06ec6ba
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 8 deletions.
2 changes: 1 addition & 1 deletion go/mysql/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,7 @@ func TestServerFlush(t *testing.T) {
flds, err := c.Fields()
require.NoError(t, err)
if duration, want := time.Since(start), 20*time.Millisecond; duration < *mysqlServerFlushDelay || duration > want {
assert.Fail(t, "duration: %v, want between %v and %v", duration, *mysqlServerFlushDelay, want)
assert.Fail(t, "duration: %v, want between %v and %v", duration.String(), (*mysqlServerFlushDelay).String(), want.String())
}
want1 := []*querypb.Field{{
Name: "result",
Expand Down
1 change: 1 addition & 0 deletions go/mysql/sql_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ var stateToMysqlCode = map[vterrors.State]struct {
vterrors.NetPacketTooLarge: {num: ERNetPacketTooLarge, state: SSNetError},
vterrors.NonUniqError: {num: ERNonUniq, state: SSConstraintViolation},
vterrors.NonUniqTable: {num: ERNonUniqTable, state: SSClientError},
vterrors.NonUpdateableTable: {num: ERNonUpdateableTable, state: SSUnknownSQLState},
vterrors.QueryInterrupted: {num: ERQueryInterrupted, state: SSQueryInterrupted},
vterrors.SPDoesNotExist: {num: ERSPDoesNotExist, state: SSClientError},
vterrors.SyntaxError: {num: ERSyntaxError, state: SSClientError},
Expand Down
9 changes: 9 additions & 0 deletions go/test/endtoend/vtgate/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,15 @@ func TestQueryAndSubQWithLimit(t *testing.T) {
assert.Equal(t, 10, len(result.Rows))
}

func TestDeleteAlias(t *testing.T) {
conn, err := mysql.Connect(context.Background(), &vtParams)
require.NoError(t, err)
defer conn.Close()

exec(t, conn, "delete t1 from t1 where id1 = 1")
exec(t, conn, "delete t.* from t1 t where t.id1 = 1")
}

func assertMatches(t *testing.T, conn *mysql.Conn, query, expected string) {
t.Helper()
qr := exec(t, conn, query)
Expand Down
13 changes: 13 additions & 0 deletions go/test/endtoend/vtgate/unsharded/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,19 @@ func TestNumericPrecisionScale(t *testing.T) {
assert.True(t, qr.Rows[0][1].Type() == sqltypes.Uint64 || qr.Rows[0][1].Type() == sqltypes.Uint32)
}

func TestDeleteAlias(t *testing.T) {
vtParams := mysql.ConnParams{
Host: "localhost",
Port: clusterInstance.VtgateMySQLPort,
}
conn, err := mysql.Connect(context.Background(), &vtParams)
require.NoError(t, err)
defer conn.Close()

exec(t, conn, "delete t1 from t1 where c1 = 1")
exec(t, conn, "delete t.* from t1 t where t.c1 = 1")
}

func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result {
t.Helper()
qr, err := conn.ExecuteFetch(query, 1000, true)
Expand Down
6 changes: 6 additions & 0 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,12 @@ var (
input: "delete /* order */ from a order by b desc",
}, {
input: "delete /* limit */ from a limit b",
}, {
input: "delete /* alias where */ t.* from a as t where t.id = 2",
output: "delete /* alias where */ t from a as t where t.id = 2",
}, {
input: "delete t.* from t, t1",
output: "delete t from t, t1",
}, {
input: "delete a from a join b on a.id = b.id where b.name = 'test'",
output: "delete a from a join b on a.id = b.id where b.`name` = 'test'",
Expand Down
1 change: 1 addition & 0 deletions go/vt/vterrors/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
IncorrectGlobalLocalVar
NonUniqError
NonUniqTable
NonUpdateableTable
SyntaxError
WrongGroupField
WrongTypeForVar
Expand Down
43 changes: 43 additions & 0 deletions go/vt/vtgate/planbuilder/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ import (
// buildDeletePlan builds the instructions for a DELETE statement.
func buildDeletePlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema ContextVSchema) (engine.Primitive, error) {
del := stmt.(*sqlparser.Delete)
var err error
if len(del.TableExprs) == 1 && len(del.Targets) == 1 {
del, err = rewriteSingleTbl(del)
if err != nil {
return nil, err
}
}
dml, ksidVindex, ksidCol, err := buildDMLPlan(vschema, "delete", del, reservedVars, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets)
if err != nil {
return nil, err
Expand Down Expand Up @@ -53,3 +60,39 @@ func buildDeletePlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedV

return edel, nil
}

func rewriteSingleTbl(del *sqlparser.Delete) (*sqlparser.Delete, error) {
atExpr, ok := del.TableExprs[0].(*sqlparser.AliasedTableExpr)
if !ok {
return del, nil
}
if !atExpr.As.IsEmpty() && !sqlparser.EqualsTableIdent(del.Targets[0].Name, atExpr.As) {
//Unknown table in MULTI DELETE
return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.UnknownTable, "Unknown table '%s' in MULTI DELETE", del.Targets[0].Name.String())
}

tbl, ok := atExpr.Expr.(sqlparser.TableName)
if !ok {
// derived table
return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NonUpdateableTable, "The target table %s of the DELETE is not updatable", atExpr.As.String())
}
if atExpr.As.IsEmpty() && !sqlparser.EqualsTableIdent(del.Targets[0].Name, tbl.Name) {
//Unknown table in MULTI DELETE
return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.UnknownTable, "Unknown table '%s' in MULTI DELETE", del.Targets[0].Name.String())
}

del.TableExprs = sqlparser.TableExprs{&sqlparser.AliasedTableExpr{Expr: tbl}}
del.Targets = nil
if del.Where != nil {
_ = sqlparser.Rewrite(del.Where, func(cursor *sqlparser.Cursor) bool {
switch node := cursor.Node().(type) {
case *sqlparser.ColName:
if !node.Qualifier.IsEmpty() {
node.Qualifier = tbl
}
}
return true
}, nil)
}
return del, nil
}
39 changes: 38 additions & 1 deletion go/vt/vtgate/planbuilder/testdata/dml_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,7 @@ Gen4 plan same as above
"KsidVindex": "user_index",
"MultiShardAutocommit": false,
"OwnedVindexQuery": "select user_id, id from music where id = 1 for update",
"Query": "delete music from music where id = 1",
"Query": "delete from music where id = 1",
"Table": "music",
"Values": [
1
Expand Down Expand Up @@ -2390,3 +2390,40 @@ Gen4 plan same as above
}
}
Gen4 plan same as above

# multi-table delete with single table
"delete u.* from user u where u.id * u.col = u.foo"
{
"QueryType": "DELETE",
"Original": "delete u.* from user u where u.id * u.col = u.foo",
"Instructions": {
"OperatorType": "Delete",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"TargetTabletType": "MASTER",
"KsidVindex": "user_index",
"MultiShardAutocommit": false,
"OwnedVindexQuery": "select Id, `Name`, Costly from `user` where `user`.id * `user`.col = `user`.foo for update",
"Query": "delete from `user` where `user`.id * `user`.col = `user`.foo",
"Table": "user"
}
}
Gen4 plan same as above

# delete with unknown reference
"delete music from user where id = 1"
"Unknown table 'music' in MULTI DELETE"
Gen4 plan same as above

# delete with derived tables
"delete music from (select * from user) music where id = 1"
"The target table music of the DELETE is not updatable"
Gen4 plan same as above

# delete with derived tables with unknown table
"delete user from (select * from user) music where id = 1"
"Unknown table 'user' in MULTI DELETE"
Gen4 plan same as above
5 changes: 0 additions & 5 deletions go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,6 @@ Gen4 plan same as above
"select func(keyspace_id) from user_index where id = :id"
"unsupported: expression on results of a vindex function"

# delete with unknown reference
"delete music from user where id = 1"
"Unknown table 'music' in MULTI DELETE"
Gen4 plan same as above

# delete with multi-table targets
"delete music,user from music inner join user where music.id = user.id"
"unsupported: multi-shard or vindex write statement"
Expand Down
3 changes: 2 additions & 1 deletion go/vt/vttablet/tabletserver/tx_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ func TestTxPoolGetConnRecentlyRemovedTransaction(t *testing.T) {
txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams())
defer txPool.Close()

conn1, _, _ = txPool.Begin(ctx, &querypb.ExecuteOptions{}, false, 0, nil)
conn1, _, err = txPool.Begin(ctx, &querypb.ExecuteOptions{}, false, 0, nil)
require.NoError(t, err, "unable to start transaction: %v", err)
conn1.Unlock()
id = conn1.ReservedID()
time.Sleep(20 * time.Millisecond)
Expand Down

0 comments on commit 06ec6ba

Please sign in to comment.