fuzz_common.tcl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. # 2007 May 10
  2. #
  3. # The author disclaims copyright to this source code. In place of
  4. # a legal notice, here is a blessing:
  5. #
  6. # May you do good and not evil.
  7. # May you find forgiveness for yourself and forgive others.
  8. # May you share freely, never taking more than you give.
  9. #
  10. #***********************************************************************
  11. #
  12. # $Id: fuzz_common.tcl,v 1.2 2009/01/05 19:36:30 drh Exp $
  13. proc fuzz {TemplateList} {
  14. set n [llength $TemplateList]
  15. set i [expr {int(rand()*$n)}]
  16. set r [uplevel 1 subst -novar [list [lindex $TemplateList $i]]]
  17. string map {"\n" " "} $r
  18. }
  19. # Fuzzy generation primitives:
  20. #
  21. # Literal
  22. # UnaryOp
  23. # BinaryOp
  24. # Expr
  25. # Table
  26. # Select
  27. # Insert
  28. #
  29. # Returns a string representing an SQL literal.
  30. #
  31. proc Literal {} {
  32. set TemplateList {
  33. 456 0 -456 1 -1
  34. 2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
  35. 'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
  36. zeroblob(1000)
  37. NULL
  38. 56.1 -56.1
  39. 123456789.1234567899
  40. }
  41. fuzz $TemplateList
  42. }
  43. # Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
  44. #
  45. proc UnaryOp {} {
  46. set TemplateList {+ - NOT ~}
  47. fuzz $TemplateList
  48. }
  49. # Returns a string containing an SQL binary operator (e.g. "*" or "/").
  50. #
  51. proc BinaryOp {} {
  52. set TemplateList {
  53. || * / % + - << >> & | < <= > >= = == != <> AND OR
  54. LIKE GLOB {NOT LIKE}
  55. }
  56. fuzz $TemplateList
  57. }
  58. # Return the complete text of an SQL expression.
  59. #
  60. set ::ExprDepth 0
  61. proc Expr { {c {}} } {
  62. incr ::ExprDepth
  63. set TemplateList [concat $c $c $c {[Literal]}]
  64. if {$::ExprDepth < 3} {
  65. lappend TemplateList \
  66. {[Expr $c] [BinaryOp] [Expr $c]} \
  67. {[UnaryOp] [Expr $c]} \
  68. {[Expr $c] ISNULL} \
  69. {[Expr $c] NOTNULL} \
  70. {CAST([Expr $c] AS blob)} \
  71. {CAST([Expr $c] AS text)} \
  72. {CAST([Expr $c] AS integer)} \
  73. {CAST([Expr $c] AS real)} \
  74. {abs([Expr])} \
  75. {coalesce([Expr], [Expr])} \
  76. {hex([Expr])} \
  77. {length([Expr])} \
  78. {lower([Expr])} \
  79. {upper([Expr])} \
  80. {quote([Expr])} \
  81. {random()} \
  82. {randomblob(min(max([Expr],1), 500))} \
  83. {typeof([Expr])} \
  84. {substr([Expr],[Expr],[Expr])} \
  85. {CASE WHEN [Expr $c] THEN [Expr $c] ELSE [Expr $c] END} \
  86. {[Literal]} {[Literal]} {[Literal]} \
  87. {[Literal]} {[Literal]} {[Literal]} \
  88. {[Literal]} {[Literal]} {[Literal]} \
  89. {[Literal]} {[Literal]} {[Literal]}
  90. }
  91. if {$::SelectDepth < 4} {
  92. lappend TemplateList \
  93. {([Select 1])} \
  94. {[Expr $c] IN ([Select 1])} \
  95. {[Expr $c] NOT IN ([Select 1])} \
  96. {EXISTS ([Select 1])} \
  97. }
  98. set res [fuzz $TemplateList]
  99. incr ::ExprDepth -1
  100. return $res
  101. }
  102. # Return a valid table name.
  103. #
  104. set ::TableList [list]
  105. proc Table {} {
  106. set TemplateList [concat sqlite_master $::TableList]
  107. fuzz $TemplateList
  108. }
  109. # Return one of:
  110. #
  111. # "SELECT DISTINCT", "SELECT ALL" or "SELECT"
  112. #
  113. proc SelectKw {} {
  114. set TemplateList {
  115. "SELECT DISTINCT"
  116. "SELECT ALL"
  117. "SELECT"
  118. }
  119. fuzz $TemplateList
  120. }
  121. # Return a result set for a SELECT statement.
  122. #
  123. proc ResultSet {{nRes 0} {c ""}} {
  124. if {$nRes == 0} {
  125. set nRes [expr {rand()*2 + 1}]
  126. }
  127. set aRes [list]
  128. for {set ii 0} {$ii < $nRes} {incr ii} {
  129. lappend aRes [Expr $c]
  130. }
  131. join $aRes ", "
  132. }
  133. set ::SelectDepth 0
  134. set ::ColumnList [list]
  135. proc SimpleSelect {{nRes 0}} {
  136. set TemplateList {
  137. {[SelectKw] [ResultSet $nRes]}
  138. }
  139. # The ::SelectDepth variable contains the number of ancestor SELECT
  140. # statements (i.e. for a top level SELECT it is set to 0, for a
  141. # sub-select 1, for a sub-select of a sub-select 2 etc.).
  142. #
  143. # If this is already greater than 3, do not generate a complicated
  144. # SELECT statement. This tends to cause parser stack overflow (too
  145. # boring to bother with).
  146. #
  147. if {$::SelectDepth < 4} {
  148. lappend TemplateList \
  149. {[SelectKw] [ResultSet $nRes $::ColumnList] FROM ([Select])} \
  150. {[SelectKw] [ResultSet $nRes] FROM ([Select])} \
  151. {[SelectKw] [ResultSet $nRes $::ColumnList] FROM [Table]} \
  152. {
  153. [SelectKw] [ResultSet $nRes $::ColumnList]
  154. FROM ([Select])
  155. GROUP BY [Expr]
  156. HAVING [Expr]
  157. } \
  158. if {0 == $nRes} {
  159. lappend TemplateList \
  160. {[SelectKw] * FROM ([Select])} \
  161. {[SelectKw] * FROM [Table]} \
  162. {[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \
  163. {
  164. [SelectKw] *
  165. FROM [Table],[Table] AS t2
  166. WHERE [Expr $::ColumnList]
  167. } {
  168. [SelectKw] *
  169. FROM [Table] LEFT OUTER JOIN [Table] AS t2
  170. ON [Expr $::ColumnList]
  171. WHERE [Expr $::ColumnList]
  172. }
  173. }
  174. }
  175. fuzz $TemplateList
  176. }
  177. # Return a SELECT statement.
  178. #
  179. # If boolean parameter $isExpr is set to true, make sure the
  180. # returned SELECT statement returns a single column of data.
  181. #
  182. proc Select {{nMulti 0}} {
  183. set TemplateList {
  184. {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
  185. {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
  186. {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
  187. {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
  188. {[SimpleSelect $nMulti] ORDER BY [Expr] DESC}
  189. {[SimpleSelect $nMulti] ORDER BY [Expr] ASC}
  190. {[SimpleSelect $nMulti] ORDER BY [Expr] ASC, [Expr] DESC}
  191. {[SimpleSelect $nMulti] ORDER BY [Expr] LIMIT [Expr] OFFSET [Expr]}
  192. }
  193. if {$::SelectDepth < 4} {
  194. if {$nMulti == 0} {
  195. set nMulti [expr {(rand()*2)+1}]
  196. }
  197. lappend TemplateList \
  198. {[SimpleSelect $nMulti] UNION [Select $nMulti]} \
  199. {[SimpleSelect $nMulti] UNION ALL [Select $nMulti]} \
  200. {[SimpleSelect $nMulti] EXCEPT [Select $nMulti]} \
  201. {[SimpleSelect $nMulti] INTERSECT [Select $nMulti]}
  202. }
  203. incr ::SelectDepth
  204. set res [fuzz $TemplateList]
  205. incr ::SelectDepth -1
  206. set res
  207. }
  208. # Generate and return a fuzzy INSERT statement.
  209. #
  210. proc Insert {} {
  211. set TemplateList {
  212. {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);}
  213. {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);}
  214. {INSERT INTO [Table] VALUES([Expr], [Expr]);}
  215. }
  216. fuzz $TemplateList
  217. }
  218. proc Column {} {
  219. fuzz $::ColumnList
  220. }
  221. # Generate and return a fuzzy UPDATE statement.
  222. #
  223. proc Update {} {
  224. set TemplateList {
  225. {UPDATE [Table]
  226. SET [Column] = [Expr $::ColumnList]
  227. WHERE [Expr $::ColumnList]}
  228. }
  229. fuzz $TemplateList
  230. }
  231. proc Delete {} {
  232. set TemplateList {
  233. {DELETE FROM [Table] WHERE [Expr $::ColumnList]}
  234. }
  235. fuzz $TemplateList
  236. }
  237. proc Statement {} {
  238. set TemplateList {
  239. {[Update]}
  240. {[Insert]}
  241. {[Select]}
  242. {[Delete]}
  243. }
  244. fuzz $TemplateList
  245. }
  246. # Return an identifier. This just chooses randomly from a fixed set
  247. # of strings.
  248. proc Identifier {} {
  249. set TemplateList {
  250. This just chooses randomly a fixed
  251. We would also thank the developers
  252. for their analysis Samba
  253. }
  254. fuzz $TemplateList
  255. }
  256. proc Check {} {
  257. # Use a large value for $::SelectDepth, because sub-selects are
  258. # not allowed in expressions used by CHECK constraints.
  259. #
  260. set sd $::SelectDepth
  261. set ::SelectDepth 500
  262. set TemplateList {
  263. {}
  264. {CHECK ([Expr])}
  265. }
  266. set res [fuzz $TemplateList]
  267. set ::SelectDepth $sd
  268. set res
  269. }
  270. proc Coltype {} {
  271. set TemplateList {
  272. {INTEGER PRIMARY KEY}
  273. {VARCHAR [Check]}
  274. {PRIMARY KEY}
  275. }
  276. fuzz $TemplateList
  277. }
  278. proc DropTable {} {
  279. set TemplateList {
  280. {DROP TABLE IF EXISTS [Identifier]}
  281. }
  282. fuzz $TemplateList
  283. }
  284. proc CreateView {} {
  285. set TemplateList {
  286. {CREATE VIEW [Identifier] AS [Select]}
  287. }
  288. fuzz $TemplateList
  289. }
  290. proc DropView {} {
  291. set TemplateList {
  292. {DROP VIEW IF EXISTS [Identifier]}
  293. }
  294. fuzz $TemplateList
  295. }
  296. proc CreateTable {} {
  297. set TemplateList {
  298. {CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])}
  299. {CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])}
  300. }
  301. fuzz $TemplateList
  302. }
  303. proc CreateOrDropTableOrView {} {
  304. set TemplateList {
  305. {[CreateTable]}
  306. {[DropTable]}
  307. {[CreateView]}
  308. {[DropView]}
  309. }
  310. fuzz $TemplateList
  311. }
  312. ########################################################################
  313. set ::log [open fuzzy.log w]
  314. #
  315. # Usage: do_fuzzy_test <testname> ?<options>?
  316. #
  317. # -template
  318. # -errorlist
  319. # -repeats
  320. #
  321. proc do_fuzzy_test {testname args} {
  322. set ::fuzzyopts(-errorlist) [list]
  323. set ::fuzzyopts(-repeats) $::REPEATS
  324. array set ::fuzzyopts $args
  325. lappend ::fuzzyopts(-errorlist) {parser stack overflow}
  326. lappend ::fuzzyopts(-errorlist) {ORDER BY}
  327. lappend ::fuzzyopts(-errorlist) {GROUP BY}
  328. lappend ::fuzzyopts(-errorlist) {datatype mismatch}
  329. for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
  330. do_test ${testname}.$ii {
  331. set ::sql [subst $::fuzzyopts(-template)]
  332. puts $::log $::sql
  333. flush $::log
  334. set rc [catch {execsql $::sql} msg]
  335. set e 1
  336. if {$rc} {
  337. set e 0
  338. foreach error $::fuzzyopts(-errorlist) {
  339. if {[string first $error $msg]>=0} {
  340. set e 1
  341. break
  342. }
  343. }
  344. }
  345. if {$e == 0} {
  346. puts ""
  347. puts $::sql
  348. puts $msg
  349. }
  350. set e
  351. } {1}
  352. }
  353. }