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