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.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. "fmt"
  22. "strings"
  23. "github.com/goccy/go-json"
  24. "github.com/seata/seata-go/pkg/datasource/sql/datasource"
  25. "github.com/seata/seata-go/pkg/datasource/sql/types"
  26. "github.com/seata/seata-go/pkg/datasource/sql/undo"
  27. "github.com/seata/seata-go/pkg/util/log"
  28. )
  29. var _ undo.UndoExecutor = (*BaseExecutor)(nil)
  30. const (
  31. checkSQLTemplate = "SELECT * FROM %s WHERE %s FOR UPDATE"
  32. maxInSize = 1000
  33. )
  34. type BaseExecutor struct {
  35. sqlUndoLog undo.SQLUndoLog
  36. undoImage *types.RecordImage
  37. }
  38. // ExecuteOn
  39. func (b *BaseExecutor) ExecuteOn(ctx context.Context, dbType types.DBType, conn *sql.Conn) error {
  40. // check data if valid
  41. return nil
  42. }
  43. // UndoPrepare
  44. func (b *BaseExecutor) UndoPrepare(undoPST *sql.Stmt, undoValues []types.ColumnImage, pkValueList []types.ColumnImage) {
  45. }
  46. func (b *BaseExecutor) dataValidationAndGoOn(ctx context.Context, conn *sql.Conn) (bool, error) {
  47. beforeImage := b.sqlUndoLog.BeforeImage
  48. afterImage := b.sqlUndoLog.AfterImage
  49. equals, err := IsRecordsEquals(beforeImage, afterImage)
  50. if err != nil {
  51. return false, err
  52. }
  53. if equals {
  54. log.Infof("Stop rollback because there is no data change between the before data snapshot and the after data snapshot.")
  55. return false, nil
  56. }
  57. // Validate if data is dirty.
  58. currentImage, err := b.queryCurrentRecords(ctx, conn)
  59. if err != nil {
  60. return false, err
  61. }
  62. // compare with current data and after image.
  63. equals, err = IsRecordsEquals(afterImage, currentImage)
  64. if err != nil {
  65. return false, err
  66. }
  67. if !equals {
  68. // If current data is not equivalent to the after data, then compare the current data with the before
  69. // data, too. No need continue to undo if current data is equivalent to the before data snapshot
  70. equals, err = IsRecordsEquals(beforeImage, currentImage)
  71. if err != nil {
  72. return false, err
  73. }
  74. if equals {
  75. log.Infof("Stop rollback because there is no data change between the before data snapshot and the current data snapshot.")
  76. // no need continue undo.
  77. return false, nil
  78. } else {
  79. oldRowJson, _ := json.Marshal(afterImage.Rows)
  80. newRowJson, _ := json.Marshal(currentImage.Rows)
  81. log.Infof("check dirty data failed, old and new data are not equal, "+
  82. "tableName:[%s], oldRows:[%s],newRows:[%s].", afterImage.TableName, oldRowJson, newRowJson)
  83. return false, fmt.Errorf("Has dirty records when undo.")
  84. }
  85. }
  86. return true, nil
  87. }
  88. func (b *BaseExecutor) queryCurrentRecords(ctx context.Context, conn *sql.Conn) (*types.RecordImage, error) {
  89. if b.undoImage == nil {
  90. return nil, fmt.Errorf("undo image is nil")
  91. }
  92. tableMeta := b.undoImage.TableMeta
  93. pkNameList := tableMeta.GetPrimaryKeyOnlyName()
  94. pkValues := b.parsePkValues(b.undoImage.Rows, pkNameList)
  95. if len(pkValues) == 0 {
  96. return nil, nil
  97. }
  98. var rowSize int
  99. for _, images := range pkValues {
  100. rowSize = len(images)
  101. break
  102. }
  103. where := buildWhereConditionByPKs(pkNameList, rowSize, maxInSize)
  104. checkSQL := fmt.Sprintf(checkSQLTemplate, b.undoImage.TableName, where)
  105. params := buildPKParams(b.undoImage.Rows, pkNameList)
  106. rows, err := conn.QueryContext(ctx, checkSQL, params...)
  107. if err != nil {
  108. return nil, err
  109. }
  110. image := types.RecordImage{
  111. TableName: b.undoImage.TableName,
  112. TableMeta: tableMeta,
  113. SQLType: types.SQLTypeSelect,
  114. }
  115. rowImages := make([]types.RowImage, 0)
  116. for rows.Next() {
  117. columnTypes, err := rows.ColumnTypes()
  118. if err != nil {
  119. return nil, err
  120. }
  121. slice := datasource.GetScanSlice(columnTypes)
  122. if err = rows.Scan(slice...); err != nil {
  123. return nil, err
  124. }
  125. colNames, err := rows.Columns()
  126. if err != nil {
  127. return nil, err
  128. }
  129. columns := make([]types.ColumnImage, 0)
  130. for i, val := range slice {
  131. columns = append(columns, types.ColumnImage{
  132. ColumnName: colNames[i],
  133. Value: val,
  134. })
  135. }
  136. rowImages = append(rowImages, types.RowImage{Columns: columns})
  137. }
  138. image.Rows = rowImages
  139. return &image, nil
  140. }
  141. func (b *BaseExecutor) parsePkValues(rows []types.RowImage, pkNameList []string) map[string][]types.ColumnImage {
  142. pkValues := make(map[string][]types.ColumnImage)
  143. // todo optimize 3 fors
  144. for _, row := range rows {
  145. for _, column := range row.Columns {
  146. for _, pk := range pkNameList {
  147. if strings.EqualFold(pk, column.ColumnName) {
  148. values := pkValues[strings.ToUpper(pk)]
  149. if values == nil {
  150. values = make([]types.ColumnImage, 0)
  151. }
  152. values = append(values, column)
  153. pkValues[pk] = values
  154. }
  155. }
  156. }
  157. }
  158. return pkValues
  159. }