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.

basic_undo_log_builder.go 7.6 kB

the ability to automatically run unit tests after creating a pull request. (#764) * feat: add unit test workflow * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * Optimize/at build lock key performance (#837) * Refer to buildlockkey2 optimization #829 * Time complexity O(NM)-> O(NK) about buildlockkey and buildlockkey2 Increased readability #829 * update import sort #829 * update Encapsulation into util packages #829 * Support Update join (#761) * duplicate image row for update join * update join condition placeholder param error * update join bugfix * Open test annotations * recover update executor * recover update test * recover update test * modified version param --------- Co-authored-by: JayLiu <38887641+luky116@users.noreply.github.com> Co-authored-by: FengZhang <zfcode@qq.com> --------- Co-authored-by: jimin <slievrly@163.com> Co-authored-by: JayLiu <38887641+luky116@users.noreply.github.com> Co-authored-by: FengZhang <zfcode@qq.com> Co-authored-by: Wiggins <125641755+MinatoWu@users.noreply.github.com> Co-authored-by: lxfeng1997 <33981743+lxfeng1997@users.noreply.github.com>
3 months ago
the ability to automatically run unit tests after creating a pull request. (#764) * feat: add unit test workflow * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * feat:the ability to automatically run unit tests after creating a pull request. * Optimize/at build lock key performance (#837) * Refer to buildlockkey2 optimization #829 * Time complexity O(NM)-> O(NK) about buildlockkey and buildlockkey2 Increased readability #829 * update import sort #829 * update Encapsulation into util packages #829 * Support Update join (#761) * duplicate image row for update join * update join condition placeholder param error * update join bugfix * Open test annotations * recover update executor * recover update test * recover update test * modified version param --------- Co-authored-by: JayLiu <38887641+luky116@users.noreply.github.com> Co-authored-by: FengZhang <zfcode@qq.com> --------- Co-authored-by: jimin <slievrly@163.com> Co-authored-by: JayLiu <38887641+luky116@users.noreply.github.com> Co-authored-by: FengZhang <zfcode@qq.com> Co-authored-by: Wiggins <125641755+MinatoWu@users.noreply.github.com> Co-authored-by: lxfeng1997 <33981743+lxfeng1997@users.noreply.github.com>
3 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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 builder
  18. import (
  19. "bytes"
  20. "database/sql"
  21. "database/sql/driver"
  22. "fmt"
  23. "github.com/arana-db/parser/ast"
  24. "github.com/arana-db/parser/test_driver"
  25. gxsort "github.com/dubbogo/gost/sort"
  26. "io"
  27. "seata.apache.org/seata-go/pkg/datasource/sql/util"
  28. "strings"
  29. "seata.apache.org/seata-go/pkg/datasource/sql/types"
  30. )
  31. // todo the executor should be stateful
  32. type BasicUndoLogBuilder struct{}
  33. // GetScanSlice get the column type for scann
  34. // todo to use ColumnInfo get slice
  35. func (*BasicUndoLogBuilder) GetScanSlice(columnNames []string, tableMeta *types.TableMeta) []driver.Value {
  36. scanSlice := make([]driver.Value, 0, len(columnNames))
  37. for _, columnNmae := range columnNames {
  38. var (
  39. scanVal interface{}
  40. // Gets Meta information about the column from metData
  41. columnMeta = tableMeta.Columns[columnNmae]
  42. )
  43. switch strings.ToUpper(columnMeta.DatabaseTypeString) {
  44. case "VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT", "JSON", "TINYTEXT":
  45. scanVal = sql.RawBytes{}
  46. case "BIT", "INT", "LONGBLOB", "SMALLINT", "TINYINT", "BIGINT", "MEDIUMINT":
  47. if columnMeta.IsNullable == 0 {
  48. scanVal = int64(0)
  49. } else {
  50. scanVal = sql.NullInt64{}
  51. }
  52. case "DATE", "DATETIME", "TIME", "TIMESTAMP", "YEAR":
  53. scanVal = sql.NullTime{}
  54. case "DECIMAL", "DOUBLE", "FLOAT":
  55. if columnMeta.IsNullable == 0 {
  56. scanVal = float64(0)
  57. } else {
  58. scanVal = sql.NullFloat64{}
  59. }
  60. default:
  61. scanVal = sql.RawBytes{}
  62. }
  63. scanSlice = append(scanSlice, &scanVal)
  64. }
  65. return scanSlice
  66. }
  67. func (b *BasicUndoLogBuilder) buildSelectArgs(stmt *ast.SelectStmt, args []driver.Value) []driver.Value {
  68. var (
  69. selectArgsIndexs = make([]int32, 0)
  70. selectArgs = make([]driver.Value, 0)
  71. )
  72. b.traversalArgs(stmt.Where, &selectArgsIndexs)
  73. if stmt.OrderBy != nil {
  74. for _, item := range stmt.OrderBy.Items {
  75. b.traversalArgs(item, &selectArgsIndexs)
  76. }
  77. }
  78. if stmt.Limit != nil {
  79. if stmt.Limit.Offset != nil {
  80. b.traversalArgs(stmt.Limit.Offset, &selectArgsIndexs)
  81. }
  82. if stmt.Limit.Count != nil {
  83. b.traversalArgs(stmt.Limit.Count, &selectArgsIndexs)
  84. }
  85. }
  86. // sort selectArgs index array
  87. gxsort.Int32(selectArgsIndexs)
  88. for _, index := range selectArgsIndexs {
  89. selectArgs = append(selectArgs, args[index])
  90. }
  91. return selectArgs
  92. }
  93. // todo perfect all sql operation
  94. func (b *BasicUndoLogBuilder) traversalArgs(node ast.Node, argsIndex *[]int32) {
  95. if node == nil {
  96. return
  97. }
  98. switch node.(type) {
  99. case *ast.BinaryOperationExpr:
  100. expr := node.(*ast.BinaryOperationExpr)
  101. b.traversalArgs(expr.L, argsIndex)
  102. b.traversalArgs(expr.R, argsIndex)
  103. break
  104. case *ast.BetweenExpr:
  105. expr := node.(*ast.BetweenExpr)
  106. b.traversalArgs(expr.Left, argsIndex)
  107. b.traversalArgs(expr.Right, argsIndex)
  108. break
  109. case *ast.PatternInExpr:
  110. exprs := node.(*ast.PatternInExpr).List
  111. for i := 0; i < len(exprs); i++ {
  112. b.traversalArgs(exprs[i], argsIndex)
  113. }
  114. break
  115. case *ast.ParenthesesExpr:
  116. expr := node.(*ast.ParenthesesExpr)
  117. b.traversalArgs(expr.Expr, argsIndex)
  118. break
  119. case *test_driver.ParamMarkerExpr:
  120. *argsIndex = append(*argsIndex, int32(node.(*test_driver.ParamMarkerExpr).Order))
  121. break
  122. }
  123. }
  124. func (b *BasicUndoLogBuilder) buildRecordImages(rowsi driver.Rows, tableMetaData *types.TableMeta) (*types.RecordImage, error) {
  125. // select column names
  126. columnNames := rowsi.Columns()
  127. rowImages := make([]types.RowImage, 0)
  128. ss := b.GetScanSlice(columnNames, tableMetaData)
  129. for {
  130. err := rowsi.Next(ss)
  131. if err == io.EOF {
  132. break
  133. }
  134. if err != nil {
  135. return nil, err
  136. }
  137. columns := make([]types.ColumnImage, 0)
  138. // build record image
  139. for i, name := range columnNames {
  140. columnMeta := tableMetaData.Columns[name]
  141. keyType := types.IndexTypeNull
  142. if _, ok := tableMetaData.GetPrimaryKeyMap()[name]; ok {
  143. keyType = types.IndexTypePrimaryKey
  144. }
  145. jdbcType := types.MySQLStrToJavaType(columnMeta.DatabaseTypeString)
  146. columns = append(columns, types.ColumnImage{
  147. KeyType: keyType,
  148. ColumnName: name,
  149. ColumnType: jdbcType,
  150. Value: ss[i],
  151. })
  152. }
  153. rowImages = append(rowImages, types.RowImage{Columns: columns})
  154. }
  155. return &types.RecordImage{TableName: tableMetaData.TableName, Rows: rowImages}, nil
  156. }
  157. // buildWhereConditionByPKs build where condition by primary keys
  158. // each pk is a condition.the result will like :" (id,userCode) in ((?,?),(?,?)) or (id,userCode) in ((?,?),(?,?) ) or (id,userCode) in ((?,?))"
  159. func (b *BasicUndoLogBuilder) buildWhereConditionByPKs(pkNameList []string, rowSize int, dbType string, maxInSize int) string {
  160. var (
  161. whereStr = &strings.Builder{}
  162. batchSize = rowSize/maxInSize + 1
  163. )
  164. if rowSize%maxInSize == 0 {
  165. batchSize = rowSize / maxInSize
  166. }
  167. for batch := 0; batch < batchSize; batch++ {
  168. if batch > 0 {
  169. whereStr.WriteString(" OR ")
  170. }
  171. whereStr.WriteString("(")
  172. for i := 0; i < len(pkNameList); i++ {
  173. if i > 0 {
  174. whereStr.WriteString(",")
  175. }
  176. // todo add escape
  177. whereStr.WriteString(fmt.Sprintf("`%s`", pkNameList[i]))
  178. }
  179. whereStr.WriteString(") IN (")
  180. var eachSize int
  181. if batch == batchSize-1 {
  182. if rowSize%maxInSize == 0 {
  183. eachSize = maxInSize
  184. } else {
  185. eachSize = rowSize % maxInSize
  186. }
  187. } else {
  188. eachSize = maxInSize
  189. }
  190. for i := 0; i < eachSize; i++ {
  191. if i > 0 {
  192. whereStr.WriteString(",")
  193. }
  194. whereStr.WriteString("(")
  195. for j := 0; j < len(pkNameList); j++ {
  196. if j > 0 {
  197. whereStr.WriteString(",")
  198. }
  199. whereStr.WriteString("?")
  200. }
  201. whereStr.WriteString(")")
  202. }
  203. whereStr.WriteString(")")
  204. }
  205. return whereStr.String()
  206. }
  207. func (b *BasicUndoLogBuilder) buildPKParams(rows []types.RowImage, pkNameList []string) []driver.Value {
  208. params := make([]driver.Value, 0)
  209. for _, row := range rows {
  210. coumnMap := row.GetColumnMap()
  211. for _, pk := range pkNameList {
  212. col := coumnMap[pk]
  213. if col != nil {
  214. params = append(params, col.Value)
  215. }
  216. }
  217. }
  218. return params
  219. }
  220. // the string as local key. the local key example(multi pk): "t_user:1_a,2_b"
  221. func (b *BasicUndoLogBuilder) buildLockKey(rows driver.Rows, meta types.TableMeta) string {
  222. var (
  223. lockKeys bytes.Buffer
  224. filedSequence int
  225. )
  226. lockKeys.WriteString(meta.TableName)
  227. lockKeys.WriteString(":")
  228. pks := b.GetScanSlice(meta.GetPrimaryKeyOnlyName(), &meta)
  229. for {
  230. err := rows.Next(pks)
  231. if err == io.EOF {
  232. break
  233. }
  234. if filedSequence > 0 {
  235. lockKeys.WriteString(",")
  236. }
  237. pkSplitIndex := 0
  238. for _, value := range pks {
  239. if pkSplitIndex > 0 {
  240. lockKeys.WriteString("_")
  241. }
  242. lockKeys.WriteString(fmt.Sprintf("%v", value))
  243. pkSplitIndex++
  244. }
  245. filedSequence++
  246. }
  247. return lockKeys.String()
  248. }
  249. // the string as local key. the local key example(multi pk): "t_user:1_a,2_b"
  250. func (b *BasicUndoLogBuilder) buildLockKey2(records *types.RecordImage, meta types.TableMeta) string {
  251. return util.BuildLockKey(records, meta)
  252. }