diff --git a/kv/union_iter.go b/kv/union_iter.go index 3310de0cbc62f..d592ce19ce97c 100644 --- a/kv/union_iter.go +++ b/kv/union_iter.go @@ -102,7 +102,7 @@ func (iter *UnionIter) updateCur() { } else { // record from dirty comes first if len(iter.dirtyIt.Value()) == 0 { - log.Warnf("delete a record not exists? k = %s", string(iter.dirtyIt.Key())) + log.Warnf("delete a record not exists? k = %q", iter.dirtyIt.Key()) // jump over this deletion iter.dirtyNext() continue diff --git a/stmt/stmts/delete.go b/stmt/stmts/delete.go index 1ad7f85dd2604..584b3a8b84985 100644 --- a/stmt/stmts/delete.go +++ b/stmt/stmts/delete.go @@ -25,6 +25,7 @@ import ( "github.com/pingcap/tidb/column" "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/field" "github.com/pingcap/tidb/plan" "github.com/pingcap/tidb/plan/plans" "github.com/pingcap/tidb/rset" @@ -125,16 +126,15 @@ func (s *DeleteStmt) planMultiTable(ctx context.Context) (plan.Plan, error) { return r, nil } -func (s *DeleteStmt) hitWhere(ctx context.Context, t table.Table, data []interface{}) (bool, error) { +func (s *DeleteStmt) hitWhere(ctx context.Context, fields []*field.ResultField, data []interface{}) (bool, error) { if s.Where == nil { return true, nil } m := map[interface{}]interface{}{} - - // Set parameter for evaluating expression. - for _, col := range t.Cols() { - m[col.Name.L] = data[col.Offset] + m[expression.ExprEvalIdentFunc] = func(name string) (interface{}, error) { + return plans.GetIdentValue(name, fields, data, field.DefaultFieldFlag) } + ok, err := expression.EvalBoolExpr(ctx, s.Where, m) if err != nil { return false, errors.Trace(err) @@ -186,7 +186,7 @@ func (s *DeleteStmt) tryDeleteUsingIndex(ctx context.Context, t table.Table) (bo return false, err } log.Infof("try delete with index id:%d, ctx:%s", handle, ctx) - ok, err := s.hitWhere(ctx, t, data) + ok, err := s.hitWhere(ctx, p.GetFields(), data) if err != nil { return false, errors.Trace(err) } @@ -204,6 +204,7 @@ func (s *DeleteStmt) tryDeleteUsingIndex(ctx context.Context, t table.Table) (bo // Exec implements the stmt.Statement Exec interface. func (s *DeleteStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { + // TODO: unify single from and multi from together. if s.MultiTable { return s.execMultiTable(ctx) } @@ -222,12 +223,14 @@ func (s *DeleteStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { return nil, nil } + fields := field.ColsToResultFields(t.Cols(), t.TableName().L) + var cnt uint64 err = t.IterRecords(ctx, t.FirstKey(), t.Cols(), func(h int64, data []interface{}, cols []*column.Col) (bool, error) { if s.Limit != nil && cnt >= s.Limit.Count { return false, nil } - ok, err = s.hitWhere(ctx, t, data) + ok, err = s.hitWhere(ctx, fields, data) if err != nil { return false, errors.Trace(err) } @@ -294,6 +297,7 @@ func (s *DeleteStmt) execMultiTable(ctx context.Context) (_ rset.Recordset, err if row == nil { break } + for _, entry := range row.RowKeys { tid := entry.Tbl.TableID() if _, ok := tblIDMap[tid]; !ok { @@ -302,6 +306,7 @@ func (s *DeleteStmt) execMultiTable(ctx context.Context) (_ rset.Recordset, err rowKeyMap[entry.Key] = entry.Tbl } } + for k, t := range rowKeyMap { handle, err := util.DecodeHandleFromRowKey(k) if err != nil { @@ -316,5 +321,6 @@ func (s *DeleteStmt) execMultiTable(ctx context.Context) (_ rset.Recordset, err return nil, errors.Trace(err) } } + return nil, nil } diff --git a/stmt/stmts/delete_test.go b/stmt/stmts/delete_test.go index 6a9686287de32..eea4522028b94 100644 --- a/stmt/stmts/delete_test.go +++ b/stmt/stmts/delete_test.go @@ -87,6 +87,10 @@ func (s *testStmtSuite) TestDelete(c *C) { r = mustExec(c, s.testDB, `DELETE from test where 0;`) checkResult(c, r, 0, 0) + mustExec(c, s.testDB, "insert into test values (2, 'abc')") + r = mustExec(c, s.testDB, `delete from test where test.id = 2 limit 1`) + checkResult(c, r, 1, 0) + // Select data tx := mustBegin(c, s.testDB) rows, err := tx.Query(s.selectSql) @@ -157,3 +161,43 @@ func (s *testStmtSuite) TestMultiTableDelete(c *C) { c.Assert(cnt, Equals, 3) rows.Close() } + +func (s *testStmtSuite) TestQualifedDelete(c *C) { + mustExec(c, s.testDB, "drop table if exists t1") + mustExec(c, s.testDB, "drop table if exists t2") + mustExec(c, s.testDB, "create table t1 (c1 int, c2 int, index (c1))") + mustExec(c, s.testDB, "create table t2 (c1 int, c2 int)") + mustExec(c, s.testDB, "insert into t1 values (1, 1), (2, 2)") + + // delete with index + r := mustExec(c, s.testDB, "delete from t1 where t1.c1 = 1") + checkResult(c, r, 1, 0) + + // delete with no index + r = mustExec(c, s.testDB, "delete from t1 where t1.c2 = 2") + checkResult(c, r, 1, 0) + + rows, err := s.testDB.Query("select * from t1") + c.Assert(err, IsNil) + cnt := 0 + for rows.Next() { + cnt++ + } + rows.Close() + c.Assert(cnt, Equals, 0) + + _, err = s.testDB.Exec("delete from t1 as a where a.c1 = 1") + c.Assert(err, NotNil) + + mustExec(c, s.testDB, "insert into t1 values (1, 1), (2, 2)") + mustExec(c, s.testDB, "insert into t2 values (2, 1), (3,1)") + + r = mustExec(c, s.testDB, "delete t1, t2 from t1 join t2 where t1.c1 = t2.c2") + checkResult(c, r, 3, 0) + + mustExec(c, s.testDB, "insert into t2 values (2, 1), (3,1)") + r = mustExec(c, s.testDB, "delete a, b from t1 as a join t2 as b where a.c2 = b.c1") + checkResult(c, r, 2, 0) + + mustExec(c, s.testDB, "drop table t1, t2") +}