You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

executor.go 5.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package executor
  18. import (
  19. "context"
  20. "database/sql"
  21. "database/sql/driver"
  22. "fmt"
  23. "strings"
  24. "github.com/goccy/go-json"
  25. "seata.apache.org/seata-go/pkg/datasource/sql/datasource"
  26. "seata.apache.org/seata-go/pkg/datasource/sql/types"
  27. "seata.apache.org/seata-go/pkg/datasource/sql/undo"
  28. "seata.apache.org/seata-go/pkg/util/log"
  29. )
  30. var _ undo.UndoExecutor = (*BaseExecutor)(nil)
  31. const (
  32. checkSQLTemplate = "SELECT * FROM %s WHERE %s FOR UPDATE"
  33. maxInSize = 1000
  34. )
  35. type BaseExecutor struct {
  36. sqlUndoLog undo.SQLUndoLog
  37. undoImage *types.RecordImage
  38. }
  39. // ExecuteOn
  40. func (b *BaseExecutor) ExecuteOn(ctx context.Context, dbType types.DBType, conn *sql.Conn) error {
  41. // check data if valid
  42. return nil
  43. }
  44. // UndoPrepare
  45. func (b *BaseExecutor) UndoPrepare(undoPST *sql.Stmt, undoValues []types.ColumnImage, pkValueList []types.ColumnImage) {
  46. }
  47. func (b *BaseExecutor) dataValidationAndGoOn(ctx context.Context, conn *sql.Conn) (bool, error) {
  48. if !undo.UndoConfig.DataValidation {
  49. return true, nil
  50. }
  51. beforeImage := b.sqlUndoLog.BeforeImage
  52. afterImage := b.sqlUndoLog.AfterImage
  53. equals, err := IsRecordsEquals(beforeImage, afterImage)
  54. if err != nil {
  55. return false, err
  56. }
  57. if equals {
  58. log.Infof("Stop rollback because there is no data change between the before data snapshot and the after data snapshot.")
  59. return false, nil
  60. }
  61. // Validate if data is dirty.
  62. currentImage, err := b.queryCurrentRecords(ctx, conn)
  63. if err != nil {
  64. return false, err
  65. }
  66. // compare with current data and after image.
  67. equals, err = IsRecordsEquals(afterImage, currentImage)
  68. if err != nil {
  69. return false, err
  70. }
  71. if !equals {
  72. // If current data is not equivalent to the after data, then compare the current data with the before
  73. // data, too. No need continue to undo if current data is equivalent to the before data snapshot
  74. equals, err = IsRecordsEquals(beforeImage, currentImage)
  75. if err != nil {
  76. return false, err
  77. }
  78. if equals {
  79. log.Infof("Stop rollback because there is no data change between the before data snapshot and the current data snapshot.")
  80. // no need continue undo.
  81. return false, nil
  82. } else {
  83. oldRowJson, _ := json.Marshal(afterImage.Rows)
  84. newRowJson, _ := json.Marshal(currentImage.Rows)
  85. log.Infof("check dirty data failed, old and new data are not equal, "+
  86. "tableName:[%s], oldRows:[%s],newRows:[%s].", afterImage.TableName, oldRowJson, newRowJson)
  87. return false, fmt.Errorf("Has dirty records when undo.")
  88. }
  89. }
  90. return true, nil
  91. }
  92. func (b *BaseExecutor) queryCurrentRecords(ctx context.Context, conn *sql.Conn) (*types.RecordImage, error) {
  93. if b.undoImage == nil {
  94. return nil, fmt.Errorf("undo image is nil")
  95. }
  96. tableMeta := b.undoImage.TableMeta
  97. pkNameList := tableMeta.GetPrimaryKeyOnlyName()
  98. pkValues := b.parsePkValues(b.undoImage.Rows, pkNameList)
  99. if len(pkValues) == 0 {
  100. return nil, nil
  101. }
  102. where := buildWhereConditionByPKs(pkNameList, len(b.undoImage.Rows), maxInSize)
  103. checkSQL := fmt.Sprintf(checkSQLTemplate, b.undoImage.TableName, where)
  104. params := buildPKParams(b.undoImage.Rows, pkNameList)
  105. rows, err := conn.QueryContext(ctx, checkSQL, params...)
  106. if err != nil {
  107. return nil, err
  108. }
  109. image := types.RecordImage{
  110. TableName: b.undoImage.TableName,
  111. TableMeta: tableMeta,
  112. SQLType: types.SQLTypeSelect,
  113. }
  114. rowImages := make([]types.RowImage, 0)
  115. for rows.Next() {
  116. columnTypes, err := rows.ColumnTypes()
  117. if err != nil {
  118. return nil, err
  119. }
  120. slice := datasource.GetScanSlice(columnTypes)
  121. if err = rows.Scan(slice...); err != nil {
  122. return nil, err
  123. }
  124. colNames, err := rows.Columns()
  125. if err != nil {
  126. return nil, err
  127. }
  128. columns := make([]types.ColumnImage, 0)
  129. for i, val := range slice {
  130. actualVal := val
  131. if v, ok := val.(driver.Valuer); ok {
  132. actualVal, _ = v.Value()
  133. }
  134. columns = append(columns, types.ColumnImage{
  135. ColumnName: colNames[i],
  136. Value: actualVal,
  137. })
  138. }
  139. rowImages = append(rowImages, types.RowImage{Columns: columns})
  140. }
  141. image.Rows = rowImages
  142. return &image, nil
  143. }
  144. func (b *BaseExecutor) parsePkValues(rows []types.RowImage, pkNameList []string) map[string][]types.ColumnImage {
  145. pkValues := make(map[string][]types.ColumnImage)
  146. // todo optimize 3 fors
  147. for _, row := range rows {
  148. for _, column := range row.Columns {
  149. for _, pk := range pkNameList {
  150. if strings.EqualFold(pk, column.ColumnName) {
  151. values := pkValues[strings.ToUpper(pk)]
  152. if values == nil {
  153. values = make([]types.ColumnImage, 0)
  154. }
  155. values = append(values, column)
  156. pkValues[pk] = values
  157. }
  158. }
  159. }
  160. }
  161. return pkValues
  162. }