/ Check-in [eada284b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix a bug introduced by the fts3 refactoring (segfault when creating a table with zero module args). Also a fix to handle an OOM error.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: eada284bc10cafcab9beb3473bb0c70b3b4de2f9
User & Date: dan 2009-11-28 12:40:32
Context
2009-11-28
13:46
Initialize a variable (unnecessarily) to avoid a compiler warning. check-in: db65fd59 user: drh tags: trunk
12:40
Fix a bug introduced by the fts3 refactoring (segfault when creating a table with zero module args). Also a fix to handle an OOM error. check-in: eada284b user: dan tags: trunk
2009-11-27
18:31
Change the test numbers in e_fkey.test so that they are in order. check-in: ca73be44 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

   630    630   ** methods of the FTS3 virtual table.
   631    631   **
   632    632   ** The argv[] array contains the following:
   633    633   **
   634    634   **   argv[0]   -> module name
   635    635   **   argv[1]   -> database name
   636    636   **   argv[2]   -> table name
   637         -**   argv[...] -> "column name" fields...
          637  +**   argv[...] -> "column name" and other module argument fields.
   638    638   */
   639    639   int fts3InitVtab(
   640    640     int isCreate,                   /* True for xCreate, false for xConnect */
   641    641     sqlite3 *db,                    /* The SQLite database connection */
   642    642     void *pAux,                     /* Hash table containing tokenizers */
   643    643     int argc,                       /* Number of elements in argv array */
   644    644     const char * const *argv,       /* xCreate/xConnect argument array */
   645    645     sqlite3_vtab **ppVTab,          /* Write the resulting vtab structure here */
   646    646     char **pzErr                    /* Write any error message here */
   647    647   ){
   648    648     Fts3Hash *pHash = (Fts3Hash *)pAux;
   649         -  Fts3Table *p;               /* Pointer to allocated vtab */
          649  +  Fts3Table *p;                   /* Pointer to allocated vtab */
   650    650     int rc;                         /* Return code */
   651         -  int i;
   652         -  int nByte;
          651  +  int i;                          /* Iterator variable */
          652  +  int nByte;                      /* Size of allocation used for *p */
   653    653     int iCol;
   654    654     int nString = 0;
   655    655     int nCol = 0;
   656    656     char *zCsr;
   657    657     int nDb;
   658    658     int nName;
   659    659   
................................................................................
   676    676     if( zTokenizer==0 ){
   677    677       rc = sqlite3Fts3InitTokenizer(pHash, 0, &pTokenizer, 0, pzErr);
   678    678       if( rc!=SQLITE_OK ){
   679    679         return rc;
   680    680       }
   681    681       assert( pTokenizer );
   682    682     }
          683  +
          684  +  if( nCol==0 ){
          685  +    nCol = 1;
          686  +  }
   683    687   
   684    688     /* Allocate and populate the Fts3Table structure. */
   685    689     nByte = sizeof(Fts3Table) +              /* Fts3Table */
   686    690             nCol * sizeof(char *) +              /* azColumn */
   687    691             nName +                              /* zName */
   688    692             nDb +                                /* zDb */
   689    693             nString;                             /* Space for azColumn strings */
................................................................................
   722    726         zCsr[n] = '\0';
   723    727         sqlite3Fts3Dequote(zCsr);
   724    728         p->azColumn[iCol++] = zCsr;
   725    729         zCsr += n+1;
   726    730         assert( zCsr <= &((char *)p)[nByte] );
   727    731       }
   728    732     }
          733  +  if( iCol==0 ){
          734  +    assert( nCol==1 );
          735  +    p->azColumn[0] = "content";
          736  +  }
   729    737   
   730    738     /* If this is an xCreate call, create the underlying tables in the 
   731    739     ** database. TODO: For xConnect(), it could verify that said tables exist.
   732    740     */
   733    741     if( isCreate ){
   734    742       rc = fts3CreateTables(p);
   735    743       if( rc!=SQLITE_OK ) goto fts3_init_out;

Changes to ext/fts3/fts3_write.c.

   405    405       }
   406    406       p->iLastCol = iCol;
   407    407       p->iLastPos = 0;
   408    408     }
   409    409     if( iCol>=0 ){
   410    410       assert( iPos>p->iLastPos || (iPos==0 && p->iLastPos==0) );
   411    411       rc = fts3PendingListAppendVarint(&p, 2+iPos-p->iLastPos);
   412         -    p->iLastPos = iPos;
          412  +    if( rc==SQLITE_OK ){
          413  +      p->iLastPos = iPos;
          414  +    }
   413    415     }
   414    416   
   415    417    pendinglistappend_out:
   416    418     *pRc = rc;
   417    419     if( p!=*pp ){
   418    420       *pp = p;
   419    421       return 1;

Added test/e_fts3.test.

            1  +# 2009 November 28
            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  +# This file implements tests to verify the "testable statements" in the
           13  +# fts3.in document.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +
           19  +ifcapable !fts3 {
           20  +  finish_test
           21  +  return
           22  +}
           23  +source $testdir/fts3_common.tcl
           24  +
           25  +set DO_MALLOC_TEST 0
           26  +
           27  +# Procs used to make the tests in this file easier to read.
           28  +#
           29  +proc ddl_test {tn ddl} {
           30  +  uplevel [list do_write_test e_fts3-$tn sqlite_master $ddl]
           31  +}
           32  +proc write_test {tn tbl sql} {
           33  +  uplevel [list do_write_test e_fts3-$tn $tbl $sql]
           34  +}
           35  +proc read_test {tn sql result} {
           36  +  uplevel [list do_select_test e_fts3-$tn $sql $result]
           37  +}
           38  +
           39  +#-----------------------------------------------------------------
           40  +# Test the example CREATE VIRTUAL TABLE statements in section 1.1 
           41  +# of fts3.in.
           42  +#
           43  +ddl_test   1.1.1 {CREATE VIRTUAL TABLE data USING fts3()}
           44  +read_test  1.1.2 {PRAGMA table_info(data)} {0 content {} 0 {} 0}
           45  +
           46  +ddl_test   1.2.1 {
           47  +  CREATE VIRTUAL TABLE pages USING fts3(title, keywords, body)
           48  +}
           49  +read_test  1.2.2 {
           50  +  PRAGMA table_info(pages)
           51  +} {0 title {} 0 {} 0 1 keywords {} 0 {} 0 2 body {} 0 {} 0}
           52  +
           53  +ddl_test   1.3.1 {
           54  +  CREATE VIRTUAL TABLE mail USING fts3(
           55  +      subject VARCHAR(256) NOT NULL,
           56  +      body TEXT CHECK(length(body)<10240)
           57  +  )
           58  +}
           59  +read_test  1.3.2 {
           60  +  PRAGMA table_info(mail)
           61  +} {0 subject {} 0 {} 0 1 body {} 0 {} 0}
           62  +
           63  +# A very large string. Used to test if the constraint on column "body" of
           64  +# table "mail" is enforced (it should not be - FTS3 tables do not support
           65  +# constraints).
           66  +set largetext [string repeat "the quick brown fox " 5000]
           67  +write_test 1.3.3 mail_content { INSERT INTO mail VALUES(NULL, $largetext) }
           68  +read_test  1.3.4 {
           69  +  SELECT subject IS NULL, length(body) FROM mail
           70  +} [list 1 100000]
           71  +
           72  +finish_test

Changes to test/fts3_common.tcl.

   282    282           foreach {t d} [fts3_readleaf $block] { lappend a($t) $d }
   283    283   
   284    284         }
   285    285       }
   286    286     }
   287    287   }
   288    288   
          289  +##########################################################################
          290  +
          291  +#-------------------------------------------------------------------------
          292  +# This proc is used to test a single SELECT statement. Parameter $name is
          293  +# passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter
          294  +# $sql is passed the text of the SELECT statement. Parameter $result is
          295  +# set to the expected output if the SELECT statement is successfully
          296  +# executed using [db eval].
          297  +#
          298  +# Example:
          299  +#
          300  +#   do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2}
          301  +#
          302  +# If global variable DO_MALLOC_TEST is set to a non-zero value, or if
          303  +# it is not defined at all, then OOM testing is performed on the SELECT
          304  +# statement. Each OOM test case is said to pass if either (a) executing
          305  +# the SELECT statement succeeds and the results match those specified
          306  +# by parameter $result, or (b) TCL throws an "out of memory" error.
          307  +#
          308  +# If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement
          309  +# is executed just once. In this case the test case passes if the results
          310  +# match the expected results passed via parameter $result.
          311  +#
          312  +proc do_select_test {name sql result} {
          313  +  doPassiveTest $name $sql [list 0 $result]
          314  +}
          315  +
          316  +proc do_error_test {name sql error} {
          317  +  doPassiveTest $name $sql [list 1 $error]
          318  +}
          319  +
          320  +proc doPassiveTest {name sql catchres} {
          321  +  if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
          322  +
          323  +  if {$::DO_MALLOC_TEST} {
          324  +    set answers [list {1 {out of memory}} $catchres]
          325  +    set modes [list 100000 transient 1 persistent]
          326  +  } else {
          327  +    set answers [list $catchres]
          328  +    set modes [list 0 nofail]
          329  +  }
          330  +  set str [join $answers " OR "]
          331  +
          332  +  foreach {nRepeat zName} $modes {
          333  +    for {set iFail 1} 1 {incr iFail} {
          334  +      if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
          335  +
          336  +      set res [catchsql $sql]
          337  +      if {[lsearch $answers $res]>=0} {
          338  +        set res $str
          339  +      }
          340  +      do_test $name.$zName.$iFail [list set {} $res] $str
          341  +      set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
          342  +      if {$nFail==0} break
          343  +    }
          344  +  }
          345  +}
          346  +
          347  +
          348  +#-------------------------------------------------------------------------
          349  +# Test a single write to the database. In this case a  "write" is a 
          350  +# DELETE, UPDATE or INSERT statement.
          351  +#
          352  +# If OOM testing is performed, there are several acceptable outcomes:
          353  +#
          354  +#   1) The write succeeds. No error is returned.
          355  +#
          356  +#   2) An "out of memory" exception is thrown and:
          357  +#
          358  +#     a) The statement has no effect, OR
          359  +#     b) The current transaction is rolled back, OR
          360  +#     c) The statement succeeds. This can only happen if the connection
          361  +#        is in auto-commit mode (after the statement is executed, so this
          362  +#        includes COMMIT statements).
          363  +#
          364  +# If the write operation eventually succeeds, zero is returned. If a
          365  +# transaction is rolled back, non-zero is returned.
          366  +#
          367  +# Parameter $name is the name to use for the test case (or test cases).
          368  +# The second parameter, $tbl, should be the name of the database table
          369  +# being modified. Parameter $sql contains the SQL statement to test.
          370  +#
          371  +proc do_write_test {name tbl sql} {
          372  +  if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
          373  +
          374  +  # Figure out an statement to get a checksum for table $tbl.
          375  +  db eval "SELECT * FROM $tbl" V break
          376  +  set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl"
          377  +
          378  +  # Calculate the initial table checksum.
          379  +  set cksum1 [db one $cksumsql]
          380  +
          381  +
          382  +  if {$::DO_MALLOC_TEST } {
          383  +    set answers [list {1 {out of memory}} {0 {}}]
          384  +    set modes [list 100000 transient 1 persistent]
          385  +  } else {
          386  +    set answers [list {0 {}}]
          387  +    set modes [list 0 nofail]
          388  +  }
          389  +  set str [join $answers " OR "]
          390  +
          391  +  foreach {nRepeat zName} $modes {
          392  +    for {set iFail 1} 1 {incr iFail} {
          393  +      if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
          394  +
          395  +      set res [uplevel [list catchsql $sql]]
          396  +      set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
          397  +      if {$nFail==0} {
          398  +        do_test $name.$zName.$iFail [list set {} $res] {0 {}}
          399  +        return
          400  +      } else {
          401  +        if {[lsearch $answers $res]>=0} {
          402  +          set res $str
          403  +        }
          404  +        do_test $name.$zName.$iFail [list set {} $res] $str
          405  +        set cksum2 [db one $cksumsql]
          406  +        if {$cksum1 != $cksum2} return
          407  +      }
          408  +    }
          409  +  }
          410  +}

Changes to test/tester.tcl.

   408    408     uplevel [list $db eval $sql]
   409    409   }
   410    410   
   411    411   # Execute SQL and catch exceptions.
   412    412   #
   413    413   proc catchsql {sql {db db}} {
   414    414     # puts "SQL = $sql"
   415         -  set r [catch {$db eval $sql} msg]
          415  +  set r [catch [list uplevel [list $db eval $sql]] msg]
   416    416     lappend r $msg
   417    417     return $r
   418    418   }
   419    419   
   420    420   # Do an VDBE code dump on the SQL given
   421    421   #
   422    422   proc explain {sql {db db}} {