builder_b_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // Copyright 2018 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package builder
  5. import (
  6. "fmt"
  7. "math/rand"
  8. "testing"
  9. )
  10. type randGenConf struct {
  11. allowCond bool
  12. allowJoin bool
  13. allowLimit bool
  14. allowUnion bool
  15. allowHaving bool
  16. allowGroupBy bool
  17. allowOrderBy bool
  18. allowSubQuery bool
  19. }
  20. var expectedValues = []interface{}{
  21. "dangerous", "fun", "degree", "hospital", "horseshoe", "summit", "parallel", "height", "recommend", "invite",
  22. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
  23. var queryFields = []string{"f1", "f2", "f2", "f4", "f5", "f6", "f7", "f8", "f9"}
  24. func BenchmarkSelect_Simple(b *testing.B) {
  25. rgc := randGenConf{allowCond: true}
  26. b.ResetTimer()
  27. for i := 0; i < b.N; i++ {
  28. randQuery("", &rgc).ToSQL()
  29. }
  30. }
  31. func BenchmarkSelect_SubQuery(b *testing.B) {
  32. rgc := randGenConf{allowSubQuery: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
  33. b.ResetTimer()
  34. for i := 0; i < b.N; i++ {
  35. randQuery("", &rgc).ToSQL()
  36. }
  37. }
  38. func BenchmarkSelect_SelectConditional4Oracle(b *testing.B) {
  39. rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
  40. for i := 0; i < b.N; i++ {
  41. randQuery(ORACLE, &rgc).ToSQL()
  42. }
  43. }
  44. func BenchmarkSelect_SelectConditional4Mssql(b *testing.B) {
  45. rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
  46. b.ResetTimer()
  47. for i := 0; i < b.N; i++ {
  48. randQuery(MSSQL, &rgc).ToSQL()
  49. }
  50. }
  51. func BenchmarkSelect_SelectConditional4MysqlLike(b *testing.B) {
  52. rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
  53. b.ResetTimer()
  54. for i := 0; i < b.N; i++ {
  55. randQuery(MYSQL, &rgc).ToSQL()
  56. }
  57. }
  58. func BenchmarkSelect_SelectConditional4Mixed(b *testing.B) {
  59. rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
  60. b.ResetTimer()
  61. for i := 0; i < b.N; i++ {
  62. randQuery(randDialect(), &rgc).ToSQL()
  63. }
  64. }
  65. func BenchmarkSelect_SelectComplex4Oracle(b *testing.B) {
  66. rgc := randGenConf{
  67. allowLimit: true, allowCond: true,
  68. allowGroupBy: true, allowHaving: true,
  69. allowOrderBy: true, allowSubQuery: true,
  70. }
  71. for i := 0; i < b.N; i++ {
  72. randQuery(ORACLE, &rgc).ToSQL()
  73. }
  74. }
  75. func BenchmarkSelect_SelectComplex4Mssql(b *testing.B) {
  76. rgc := randGenConf{
  77. allowLimit: true, allowCond: true,
  78. allowGroupBy: true, allowHaving: true,
  79. allowOrderBy: true, allowSubQuery: true,
  80. }
  81. b.ResetTimer()
  82. for i := 0; i < b.N; i++ {
  83. randQuery(MSSQL, &rgc).ToSQL()
  84. }
  85. }
  86. func BenchmarkSelect_SelectComplex4MysqlLike(b *testing.B) {
  87. rgc := randGenConf{
  88. allowLimit: true, allowCond: true,
  89. allowGroupBy: true, allowHaving: true,
  90. allowOrderBy: true, allowSubQuery: true,
  91. }
  92. b.ResetTimer()
  93. for i := 0; i < b.N; i++ {
  94. randQuery(MYSQL, &rgc).ToSQL()
  95. }
  96. }
  97. func BenchmarkSelect_SelectComplex4MysqlMixed(b *testing.B) {
  98. rgc := randGenConf{
  99. allowLimit: true, allowCond: true,
  100. allowGroupBy: true, allowHaving: true,
  101. allowOrderBy: true, allowSubQuery: true,
  102. }
  103. b.ResetTimer()
  104. for i := 0; i < b.N; i++ {
  105. randQuery(randDialect(), &rgc).ToSQL()
  106. }
  107. }
  108. func BenchmarkInsert(b *testing.B) {
  109. rgc := randGenConf{allowCond: true}
  110. b.ResetTimer()
  111. for i := 0; i < b.N; i++ {
  112. randInsertByCondition(&rgc).ToSQL()
  113. }
  114. }
  115. func BenchmarkUpdate(b *testing.B) {
  116. rgc := randGenConf{allowCond: true}
  117. b.ResetTimer()
  118. for i := 0; i < b.N; i++ {
  119. randUpdateByCondition(&rgc).ToSQL()
  120. }
  121. }
  122. // randQuery Generate a basic query for benchmark test. But be careful it's not a executable SQL in real db.
  123. func randQuery(dialect string, rgc *randGenConf) *Builder {
  124. b := randSelectByCondition(dialect, rgc)
  125. isUnionized := rgc.allowUnion && rand.Intn(1000) >= 500
  126. if isUnionized {
  127. r := rand.Intn(3) + 1
  128. for i := r; i < r; i++ {
  129. b = b.Union("all", randSelectByCondition(dialect, rgc))
  130. }
  131. }
  132. if isUnionized && rgc.allowLimit && rand.Intn(1000) >= 500 {
  133. b = randLimit(Dialect(dialect).Select().From(b, "t"))
  134. }
  135. return b
  136. }
  137. func randInsertByCondition(rgc *randGenConf) *Builder {
  138. fields := randSelects()
  139. times := rand.Intn(10) + 1
  140. eqs := Eq{}
  141. for i := 0; i < times; i++ {
  142. eqs[fields[rand.Intn(len(fields))]] = "expected"
  143. }
  144. b := Insert(eqs).From("table1")
  145. if rgc.allowCond && rand.Intn(1000) >= 500 {
  146. b = b.Where(randCond(b.selects, 3))
  147. }
  148. return b
  149. }
  150. func randUpdateByCondition(rgc *randGenConf) *Builder {
  151. fields := randSelects()
  152. times := rand.Intn(10) + 1
  153. eqs := Eq{}
  154. for i := 0; i < times; i++ {
  155. eqs[fields[rand.Intn(len(fields))]] = randVal()
  156. }
  157. b := Update(eqs).From("table1")
  158. if rgc.allowCond && rand.Intn(1000) >= 500 {
  159. b.Where(randCond(fields, 3))
  160. }
  161. return b
  162. }
  163. func randSelectByCondition(dialect string, rgc *randGenConf) *Builder {
  164. var b *Builder
  165. if rgc.allowSubQuery {
  166. cpRgc := *rgc
  167. cpRgc.allowSubQuery = false
  168. b = Dialect(dialect).Select(randSelects()...).From(randQuery(dialect, &cpRgc), randTableName(0))
  169. } else {
  170. b = Dialect(dialect).Select(randSelects()...).From(randTableName(0))
  171. }
  172. if rgc.allowJoin {
  173. b = randJoin(b, 3)
  174. }
  175. if rgc.allowCond && rand.Intn(1000) >= 500 {
  176. b = b.Where(randCond(b.selects, 3))
  177. }
  178. if rgc.allowLimit && rand.Intn(1000) >= 500 {
  179. b = randLimit(b)
  180. }
  181. if rgc.allowOrderBy && rand.Intn(1000) >= 500 {
  182. b = randOrderBy(b)
  183. }
  184. if rgc.allowHaving && rand.Intn(1000) >= 500 {
  185. b = randHaving(b)
  186. }
  187. if rgc.allowGroupBy && rand.Intn(1000) >= 500 {
  188. b = randGroupBy(b)
  189. }
  190. return b
  191. }
  192. func randDialect() string {
  193. dialects := []string{MYSQL, ORACLE, MSSQL, SQLITE, POSTGRES}
  194. return dialects[rand.Intn(len(dialects))]
  195. }
  196. func randSelects() []string {
  197. if rand.Intn(1000) > 900 {
  198. return []string{"*"}
  199. }
  200. rdx := rand.Intn(len(queryFields) / 2)
  201. return queryFields[rdx:]
  202. }
  203. func randTableName(offset int) string {
  204. return fmt.Sprintf("table%v", rand.Intn(10)+offset)
  205. }
  206. func randJoin(b *Builder, lessThan int) *Builder {
  207. if lessThan <= 0 {
  208. return b
  209. }
  210. times := rand.Intn(lessThan)
  211. for i := 0; i < times; i++ {
  212. tableName := randTableName(i * 10)
  213. b = b.Join("", tableName, fmt.Sprintf("%v.id = %v.id", b.TableName(), tableName))
  214. }
  215. return b
  216. }
  217. func randCond(selects []string, lessThan int) Cond {
  218. if len(selects) <= 0 {
  219. return nil
  220. }
  221. cond := NewCond()
  222. times := rand.Intn(lessThan)
  223. for i := 0; i < times; i++ {
  224. cond = cond.And(Eq{selects[rand.Intn(len(selects))]: randVal()})
  225. }
  226. return cond
  227. }
  228. func randLimit(b *Builder) *Builder {
  229. r := rand.Intn(1000) + 1
  230. if r > 500 {
  231. return b.Limit(r, 1000)
  232. } else {
  233. return b.Limit(r)
  234. }
  235. }
  236. func randOrderBy(b *Builder) *Builder {
  237. return b.OrderBy(fmt.Sprintf("%v ASC", b.selects[rand.Intn(len(b.selects))]))
  238. }
  239. func randHaving(b *Builder) *Builder {
  240. return b.OrderBy(fmt.Sprintf("%v = %v", b.selects[rand.Intn(len(b.selects))], randVal()))
  241. }
  242. func randGroupBy(b *Builder) *Builder {
  243. return b.GroupBy(fmt.Sprintf("%v = %v", b.selects[rand.Intn(len(b.selects))], randVal()))
  244. }
  245. func randVal() interface{} {
  246. return expectedValues[rand.Intn(len(expectedValues))]
  247. }