163 lines
4.1 KiB
Go
163 lines
4.1 KiB
Go
// Copyright 2016 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package executor
|
|
|
|
import (
|
|
"github.com/juju/errors"
|
|
"github.com/pingcap/tidb/ast"
|
|
"github.com/pingcap/tidb/context"
|
|
"github.com/pingcap/tidb/expression"
|
|
"github.com/pingcap/tidb/table"
|
|
"github.com/pingcap/tidb/util/types"
|
|
)
|
|
|
|
// NewUpdateExec represents a new update executor.
|
|
type NewUpdateExec struct {
|
|
SelectExec Executor
|
|
OrderedList []*expression.Assignment
|
|
|
|
// Map for unique (Table, handle) pair.
|
|
updatedRowKeys map[table.Table]map[int64]struct{}
|
|
ctx context.Context
|
|
|
|
rows []*Row // The rows fetched from TableExec.
|
|
newRowsData [][]types.Datum // The new values to be set.
|
|
fetched bool
|
|
cursor int
|
|
}
|
|
|
|
// Schema implements Executor Schema interface.
|
|
func (e *NewUpdateExec) Schema() expression.Schema {
|
|
return nil
|
|
}
|
|
|
|
// Next implements Executor Next interface.
|
|
func (e *NewUpdateExec) Next() (*Row, error) {
|
|
if !e.fetched {
|
|
err := e.fetchRows()
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
e.fetched = true
|
|
}
|
|
|
|
assignFlag, err := getNewUpdateColumns(e.OrderedList)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
if e.cursor >= len(e.rows) {
|
|
return nil, nil
|
|
}
|
|
if e.updatedRowKeys == nil {
|
|
e.updatedRowKeys = make(map[table.Table]map[int64]struct{})
|
|
}
|
|
row := e.rows[e.cursor]
|
|
newData := e.newRowsData[e.cursor]
|
|
for _, entry := range row.RowKeys {
|
|
tbl := entry.Tbl
|
|
if e.updatedRowKeys[tbl] == nil {
|
|
e.updatedRowKeys[tbl] = make(map[int64]struct{})
|
|
}
|
|
offset := e.getTableOffset(*entry)
|
|
handle := entry.Handle
|
|
oldData := row.Data[offset : offset+len(tbl.WritableCols())]
|
|
newTableData := newData[offset : offset+len(tbl.WritableCols())]
|
|
_, ok := e.updatedRowKeys[tbl][handle]
|
|
if ok {
|
|
// Each matched row is updated once, even if it matches the conditions multiple times.
|
|
continue
|
|
}
|
|
// Update row
|
|
err1 := updateRecord(e.ctx, handle, oldData, newTableData, assignFlag, tbl, offset, false)
|
|
if err1 != nil {
|
|
return nil, errors.Trace(err1)
|
|
}
|
|
e.updatedRowKeys[tbl][handle] = struct{}{}
|
|
}
|
|
e.cursor++
|
|
return &Row{}, nil
|
|
}
|
|
|
|
func getNewUpdateColumns(assignList []*expression.Assignment) ([]bool, error) {
|
|
assignFlag := make([]bool, len(assignList))
|
|
for i, v := range assignList {
|
|
if v != nil {
|
|
assignFlag[i] = true
|
|
} else {
|
|
assignFlag[i] = false
|
|
}
|
|
}
|
|
return assignFlag, nil
|
|
}
|
|
|
|
func (e *NewUpdateExec) fetchRows() error {
|
|
for {
|
|
row, err := e.SelectExec.Next()
|
|
if err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
if row == nil {
|
|
return nil
|
|
}
|
|
data := make([]types.Datum, len(e.SelectExec.Schema()))
|
|
newData := make([]types.Datum, len(e.SelectExec.Schema()))
|
|
for i, s := range e.SelectExec.Schema() {
|
|
data[i], err = s.Eval(row.Data, e.ctx)
|
|
if err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
newData[i] = data[i]
|
|
if e.OrderedList[i] != nil {
|
|
val, err := e.OrderedList[i].Expr.Eval(row.Data, e.ctx)
|
|
if err != nil {
|
|
return errors.Trace(err)
|
|
}
|
|
newData[i] = val
|
|
}
|
|
}
|
|
row.Data = data
|
|
e.rows = append(e.rows, row)
|
|
e.newRowsData = append(e.newRowsData, newData)
|
|
}
|
|
}
|
|
|
|
func (e *NewUpdateExec) getTableOffset(entry RowKeyEntry) int {
|
|
t := entry.Tbl
|
|
var tblName string
|
|
if entry.TableAsName == nil || len(entry.TableAsName.L) == 0 {
|
|
tblName = t.Meta().Name.L
|
|
} else {
|
|
tblName = entry.TableAsName.L
|
|
}
|
|
schema := e.SelectExec.Schema()
|
|
for i := 0; i < len(schema); i++ {
|
|
s := schema[i]
|
|
if s.TblName.L == tblName {
|
|
return i
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Fields implements Executor Fields interface.
|
|
// Returns nil to indicate there is no output.
|
|
func (e *NewUpdateExec) Fields() []*ast.ResultField {
|
|
return nil
|
|
}
|
|
|
|
// Close implements Executor Close interface.
|
|
func (e *NewUpdateExec) Close() error {
|
|
return e.SelectExec.Close()
|
|
}
|