func replaceRow(ctx context.Context, t table.Table, handle int64, replaceRow []interface{}) error { row, err := t.Row(ctx, handle) if err != nil { return errors.Trace(err) } result := 0 isReplace := false touched := make(map[int]bool, len(row)) for i, val := range row { result, err = types.Compare(val, replaceRow[i]) if err != nil { return errors.Trace(err) } if result != 0 { touched[i] = true isReplace = true } } if isReplace { variable.GetSessionVars(ctx).AddAffectedRows(1) if err = t.UpdateRecord(ctx, handle, row, replaceRow, touched); err != nil { return errors.Trace(err) } } return nil }
func (s *testColumnChangeSuite) checkAddPublic(d *ddl, ctx context.Context, writeOnlyTable, publicTable table.Table) error { // publicTable Insert t values (4, 4, 4) h, err := publicTable.AddRecord(ctx, types.MakeDatums(4, 4, 4)) if err != nil { return errors.Trace(err) } err = ctx.CommitTxn() if err != nil { return errors.Trace(err) } // writeOnlyTable update t set c1 = 3 where c1 = 4 oldRow, err := writeOnlyTable.RowWithCols(ctx, h, writeOnlyTable.WritableCols()) if err != nil { return errors.Trace(err) } if len(oldRow) != 3 { return errors.Errorf("%v", oldRow) } newRow := types.MakeDatums(3, 4, oldRow[2].GetValue()) err = writeOnlyTable.UpdateRecord(ctx, h, oldRow, newRow, touchedMap(writeOnlyTable)) if err != nil { return errors.Trace(err) } err = ctx.CommitTxn() if err != nil { return errors.Trace(err) } // publicTable select * from t, make sure the new c3 value 4 is not overwritten to default value 3. err = checkResult(ctx, publicTable, testutil.RowsWithSep(" ", "2 3 3", "3 4 4")) if err != nil { return errors.Trace(err) } return nil }
func (s *testIndexChangeSuite) checkDropWriteOnly(d *ddl, ctx context.Context, publicTbl, writeTbl table.Table) error { // WriteOnlyTable insert t values (8, 8) _, err := writeTbl.AddRecord(ctx, types.MakeDatums(8, 8)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 8, 8, true) if err != nil { return errors.Trace(err) } // WriteOnlyTable update t set c2 = 7 where c1 = 8 and c2 = 8 err = writeTbl.UpdateRecord(ctx, 8, types.MakeDatums(8, 8), types.MakeDatums(8, 7), touchedMap(writeTbl)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 7, 8, true) if err != nil { return errors.Trace(err) } // WriteOnlyTable delete t where c1 = 8 err = writeTbl.RemoveRecord(ctx, 8, types.MakeDatums(8, 7)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 7, 8, false) if err != nil { return errors.Trace(err) } return nil }
func (s *testIndexChangeSuite) checkDropDeleteOnly(d *ddl, ctx context.Context, writeTbl, delTbl table.Table) error { // WriteOnlyTable insert t values (9, 9) _, err := writeTbl.AddRecord(ctx, types.MakeDatums(9, 9)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeTbl, 9, 9, true) // DeleteOnlyTable insert t values (10, 10) _, err = delTbl.AddRecord(ctx, types.MakeDatums(10, 10)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeTbl, 10, 10, false) if err != nil { return errors.Trace(err) } // DeleteOnlyTable update t set c2 = 10 where c1 = 9 err = delTbl.UpdateRecord(ctx, 9, types.MakeDatums(9, 9), types.MakeDatums(9, 10), touchedMap(delTbl)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeTbl, 9, 9, false) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeTbl, 10, 9, false) if err != nil { return errors.Trace(err) } return nil }
func (s *testIndexChangeSuite) checkAddPublic(d *ddl, ctx context.Context, writeTbl, publicTbl table.Table) error { // WriteOnlyTable: insert t values (6, 6) _, err := writeTbl.AddRecord(ctx, types.MakeDatums(6, 6)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 6, 6, true) if err != nil { return errors.Trace(err) } // PublicTable: insert t values (7, 7) _, err = publicTbl.AddRecord(ctx, types.MakeDatums(7, 7)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 7, 7, true) if err != nil { return errors.Trace(err) } // WriteOnlyTable: update t set c2 = 5 where c1 = 7 and c2 = 7 err = writeTbl.UpdateRecord(ctx, 7, types.MakeDatums(7, 7), types.MakeDatums(7, 5), touchedMap(writeTbl)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 5, 7, true) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 7, 7, false) if err != nil { return errors.Trace(err) } // WriteOnlyTable: delete t where c1 = 6 err = writeTbl.RemoveRecord(ctx, 6, types.MakeDatums(6, 6)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, publicTbl, 6, 6, false) var rows [][]types.Datum publicTbl.IterRecords(ctx, publicTbl.FirstKey(), publicTbl.Cols(), func(h int64, data []types.Datum, cols []*table.Column) (bool, error) { rows = append(rows, data) return true, nil }) if len(rows) == 0 { return errors.New("table is empty") } for _, row := range rows { idxVal := row[1].GetInt64() handle := row[0].GetInt64() err = checkIndexExists(ctx, publicTbl, idxVal, handle, true) if err != nil { return errors.Trace(err) } } return nil }
func (s *testColumnChangeSuite) checkAddWriteOnly(d *ddl, ctx context.Context, deleteOnlyTable, writeOnlyTable table.Table) error { // WriteOnlyTable: insert t values (2, 3) _, err := writeOnlyTable.AddRecord(ctx, types.MakeDatums(2, 3)) if err != nil { return errors.Trace(err) } err = ctx.CommitTxn() if err != nil { return errors.Trace(err) } err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "1 2 <nil>", "2 3 3")) if err != nil { return errors.Trace(err) } // DeleteOnlyTable: select * from t err = checkResult(ctx, deleteOnlyTable, testutil.RowsWithSep(" ", "1 2", "2 3")) if err != nil { return errors.Trace(err) } // WriteOnlyTable: update t set c1 = 2 where c1 = 1 h, _, err := writeOnlyTable.Seek(ctx, 0) if err != nil { return errors.Trace(err) } err = writeOnlyTable.UpdateRecord(ctx, h, types.MakeDatums(1, 2), types.MakeDatums(2, 2), touchedMap(writeOnlyTable)) if err != nil { return errors.Trace(err) } err = ctx.CommitTxn() if err != nil { return errors.Trace(err) } // After we update the first row, its default value is also set. err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "2 2 3", "2 3 3")) if err != nil { return errors.Trace(err) } // DeleteOnlyTable: delete from t where c2 = 2 err = deleteOnlyTable.RemoveRecord(ctx, h, types.MakeDatums(2, 2)) if err != nil { return errors.Trace(err) } err = ctx.CommitTxn() if err != nil { return errors.Trace(err) } // After delete table has deleted the first row, check the WriteOnly table records. err = checkResult(ctx, writeOnlyTable, testutil.RowsWithSep(" ", "2 3 3")) return errors.Trace(err) }
func updateRecord(ctx context.Context, h int64, oldData, newData []types.Datum, assignFlag []bool, t table.Table, offset int, onDuplicateUpdate bool) error { cols := t.Cols() touched := make(map[int]bool, len(cols)) assignExists := false sc := ctx.GetSessionVars().StmtCtx var newHandle types.Datum for i, hasSetExpr := range assignFlag { if !hasSetExpr { if onDuplicateUpdate { newData[i] = oldData[i] } continue } if i < offset || i >= offset+len(cols) { // The assign expression is for another table, not this. continue } colIndex := i - offset col := cols[colIndex] if col.IsPKHandleColumn(t.Meta()) { newHandle = newData[i] } if mysql.HasAutoIncrementFlag(col.Flag) { if newData[i].IsNull() { return errors.Errorf("Column '%v' cannot be null", col.Name.O) } val, err := newData[i].ToInt64(sc) if err != nil { return errors.Trace(err) } t.RebaseAutoID(val, true) } touched[colIndex] = true assignExists = true } // If no assign list for this table, no need to update. if !assignExists { return nil } // Check whether new value is valid. if err := table.CastValues(ctx, newData, cols, false); err != nil { return errors.Trace(err) } if err := table.CheckNotNull(cols, newData); err != nil { return errors.Trace(err) } // If row is not changed, we should do nothing. rowChanged := false for i := range oldData { if !touched[i] { continue } n, err := newData[i].CompareDatum(sc, oldData[i]) if err != nil { return errors.Trace(err) } if n != 0 { rowChanged = true break } } if !rowChanged { // See https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS if ctx.GetSessionVars().ClientCapability&mysql.ClientFoundRows > 0 { sc.AddAffectedRows(1) } return nil } var err error if !newHandle.IsNull() { err = t.RemoveRecord(ctx, h, oldData) if err != nil { return errors.Trace(err) } _, err = t.AddRecord(ctx, newData) } else { // Update record to new value and update index. err = t.UpdateRecord(ctx, h, oldData, newData, touched) } if err != nil { return errors.Trace(err) } dirtyDB := getDirtyDB(ctx) tid := t.Meta().ID dirtyDB.deleteRow(tid, h) dirtyDB.addRow(tid, h, newData) // Record affected rows. if !onDuplicateUpdate { sc.AddAffectedRows(1) } else { sc.AddAffectedRows(2) } return nil }
func updateRecord(ctx context.Context, h int64, data []interface{}, t table.Table, updateColumns map[int]expression.Assignment, m map[interface{}]interface{}, offset int, onDuplicateUpdate bool) error { if err := t.LockRow(ctx, h, true); err != nil { return errors.Trace(err) } oldData := make([]interface{}, len(t.Cols())) touched := make([]bool, len(t.Cols())) copy(oldData, data) cols := t.Cols() assignExists := false for i, asgn := range updateColumns { if i < offset || i >= offset+len(cols) { // The assign expression is for another table, not this. continue } val, err := asgn.Expr.Eval(ctx, m) if err != nil { return err } colIndex := i - offset touched[colIndex] = true data[colIndex] = val assignExists = true } // no assign list for this table, no need to update. if !assignExists { return nil } // Check whether new value is valid. if err := column.CastValues(ctx, data, t.Cols()); err != nil { return err } if err := column.CheckNotNull(t.Cols(), data); err != nil { return err } // If row is not changed, we should do nothing. rowChanged := false for i, d := range data { if !touched[i] { continue } od := oldData[i] n, err := types.Compare(d, od) if err != nil { return errors.Trace(err) } if n != 0 { rowChanged = true break } } if !rowChanged { // See: https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS if variable.GetSessionVars(ctx).ClientCapability&mysql.ClientFoundRows > 0 { variable.GetSessionVars(ctx).AddAffectedRows(1) } return nil } // Update record to new value and update index. err := t.UpdateRecord(ctx, h, oldData, data, touched) if err != nil { return errors.Trace(err) } // Record affected rows. if !onDuplicateUpdate { variable.GetSessionVars(ctx).AddAffectedRows(1) } else { variable.GetSessionVars(ctx).AddAffectedRows(2) } return nil }
func updateRecord(ctx context.Context, h int64, data []interface{}, t table.Table, tcols []*column.Col, assignList []expressions.Assignment, insertData []interface{}, args map[interface{}]interface{}) error { if err := t.LockRow(ctx, h, true); err != nil { return errors.Trace(err) } oldData := make([]interface{}, len(t.Cols())) touched := make([]bool, len(t.Cols())) copy(oldData, data) // Generate new values m := args if m == nil { m = make(map[interface{}]interface{}, len(t.Cols())) // Set parameter for evaluating expression. for _, col := range t.Cols() { m[col.Name.L] = data[col.Offset] } } if insertData != nil { m[expressions.ExprEvalValuesFunc] = func(name string) (interface{}, error) { return getInsertValue(name, t.Cols(), insertData) } } for i, asgn := range assignList { val, err := asgn.Expr.Eval(ctx, m) if err != nil { return err } colIndex := tcols[i].Offset touched[colIndex] = true data[colIndex] = val } // Check whether new value is valid. if err := column.CastValues(ctx, data, t.Cols()); err != nil { return err } if err := column.CheckNotNull(t.Cols(), data); err != nil { return err } // If row is not changed, we should do nothing. rowChanged := false for i, d := range data { if !touched[i] { continue } od := oldData[i] n, err := types.Compare(d, od) if err != nil { return errors.Trace(err) } if n != 0 { rowChanged = true break } } if !rowChanged { // See: https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS if variable.GetSessionVars(ctx).ClientCapability&mysql.ClientFoundRows > 0 { variable.GetSessionVars(ctx).AddAffectedRows(1) } return nil } // Update record to new value and update index. err := t.UpdateRecord(ctx, h, oldData, data, touched) if err != nil { return errors.Trace(err) } // Record affected rows. if len(insertData) == 0 { variable.GetSessionVars(ctx).AddAffectedRows(1) } else { variable.GetSessionVars(ctx).AddAffectedRows(2) } return nil }
func updateRecord(ctx context.Context, h int64, data []interface{}, t table.Table, updateColumns map[int]*expression.Assignment, evalMap map[interface{}]interface{}, offset int, onDuplicateUpdate bool) error { if err := t.LockRow(ctx, h); err != nil { return errors.Trace(err) } cols := t.Cols() oldData := data newData := make([]interface{}, len(cols)) touched := make(map[int]bool, len(cols)) copy(newData, oldData) assignExists := false var newHandle interface{} for i, asgn := range updateColumns { if i < offset || i >= offset+len(cols) { // The assign expression is for another table, not this. continue } val, err := asgn.Expr.Eval(ctx, evalMap) if err != nil { return errors.Trace(err) } colIndex := i - offset col := cols[colIndex] if col.IsPKHandleColumn(t.Meta()) { newHandle = val } touched[colIndex] = true newData[colIndex] = val assignExists = true } // If no assign list for this table, no need to update. if !assignExists { return nil } // Check whether new value is valid. if err := column.CastValues(ctx, newData, cols); err != nil { return errors.Trace(err) } if err := column.CheckNotNull(cols, newData); err != nil { return errors.Trace(err) } // If row is not changed, we should do nothing. rowChanged := false for i := range oldData { if !touched[i] { continue } n, err := types.Compare(newData[i], oldData[i]) if err != nil { return errors.Trace(err) } if n != 0 { rowChanged = true break } } if !rowChanged { // See: https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS if variable.GetSessionVars(ctx).ClientCapability&mysql.ClientFoundRows > 0 { variable.GetSessionVars(ctx).AddAffectedRows(1) } return nil } var err error if newHandle != nil { err = t.RemoveRecord(ctx, h, oldData) if err != nil { return errors.Trace(err) } _, err = t.AddRecord(ctx, newData) } else { // Update record to new value and update index. err = t.UpdateRecord(ctx, h, oldData, newData, touched) } if err != nil { return errors.Trace(err) } // Record affected rows. if !onDuplicateUpdate { variable.GetSessionVars(ctx).AddAffectedRows(1) } else { variable.GetSessionVars(ctx).AddAffectedRows(2) } return nil }
func (s *testIndexChangeSuite) checkAddWriteOnly(d *ddl, ctx context.Context, delOnlyTbl, writeOnlyTbl table.Table) error { // DeleteOnlyTable: insert t values (4, 4); _, err := delOnlyTbl.AddRecord(ctx, types.MakeDatums(4, 4)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeOnlyTbl, 4, 4, false) if err != nil { return errors.Trace(err) } // WriteOnlyTable: insert t values (5, 5); _, err = writeOnlyTbl.AddRecord(ctx, types.MakeDatums(5, 5)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeOnlyTbl, 5, 5, true) if err != nil { return errors.Trace(err) } // WriteOnlyTable: update t set c2 = 1 where c1 = 4 and c2 = 4 err = writeOnlyTbl.UpdateRecord(ctx, 4, types.MakeDatums(4, 4), types.MakeDatums(4, 1), touchedMap(writeOnlyTbl)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeOnlyTbl, 1, 4, true) if err != nil { return errors.Trace(err) } // DeleteOnlyTable: update t set c2 = 3 where c1 = 4 and c2 = 1 err = delOnlyTbl.UpdateRecord(ctx, 4, types.MakeDatums(4, 1), types.MakeDatums(4, 3), touchedMap(writeOnlyTbl)) if err != nil { return errors.Trace(err) } // old value index not exists. err = checkIndexExists(ctx, writeOnlyTbl, 1, 4, false) if err != nil { return errors.Trace(err) } // new value index not exists. err = checkIndexExists(ctx, writeOnlyTbl, 3, 4, false) if err != nil { return errors.Trace(err) } // WriteOnlyTable: delete t where c1 = 4 and c2 = 3 err = writeOnlyTbl.RemoveRecord(ctx, 4, types.MakeDatums(4, 3)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeOnlyTbl, 3, 4, false) if err != nil { return errors.Trace(err) } // DeleteOnlyTable: delete t where c1 = 5 err = delOnlyTbl.RemoveRecord(ctx, 5, types.MakeDatums(5, 5)) if err != nil { return errors.Trace(err) } err = checkIndexExists(ctx, writeOnlyTbl, 5, 5, false) if err != nil { return errors.Trace(err) } return nil }