Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -28,11 +28,11 @@ # are provide so that these aspects of the build process can be changed # on the "make" command-line. Ex: "make CC=clang CFLAGS=-fsanitize=undefined" # CC = @CC@ CFLAGS = @CPPFLAGS@ @CFLAGS@ -TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree +TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/fts3 # Define this for the autoconf-based build, so that the code knows it can # include the generated config.h # TCC += -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite @@ -941,22 +941,40 @@ testfixture$(TEXE): $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) - +# A very detailed test running most or all test cases fulltest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test +# Really really long testing soaktest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test -soak=1 +# Do extra testing but not aeverything. fulltestonly: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/full.test +# This is the common case. Run many tests but not those that take +# a really long time. +# test: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/veryquick.test + +# Run a test using valgrind. This can take a really long time +# because valgrind is so much slower than a native machine. +# +valgrindtest: testfixture$(TEXE) sqlite3$(TEXE) + OMIT_MISUSE=1 valgrind -v ./testfixture$(TEXE) $(TOP)/test/permutations.test valgrind + +# A very fast test that checks basic sanity. The name comes from +# the 60s-era electronics testing: "Turn it on and see if smoke +# comes out." +# +smoketest: testfixture$(TEXE) + ./testfixture$(TEXE) $(TOP)/test/main.test sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl echo "#define TCLSH 2" > $@ cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@ echo "static const char *tclsh_main_loop(void){" >> $@ Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -1669,11 +1669,11 @@ sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); pFile->ctrlFlags |= UNIXFILE_WARNED; return; } if( fileHasMoved(pFile) ){ - sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); + sqlite3_log(SQLITE_WARNING, "file renamed while open: [%s]", pFile->zPath); pFile->ctrlFlags |= UNIXFILE_WARNED; return; } } @@ -4128,10 +4128,11 @@ ** at offset (nSize-1), to set the size of the file correctly. ** This is a similar technique to that used by glibc on systems ** that do not have a real fallocate() call. */ int nBlk = buf.st_blksize; /* File-system block size */ + int nWrite = 0; /* Number of bytes written by seekAndWrite */ i64 iWrite; /* Next offset to write to */ if( robust_ftruncate(pFile->h, nSize) ){ storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); @@ -4139,15 +4140,15 @@ iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; assert( iWrite>=buf.st_size ); assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); assert( ((iWrite+1)%nBlk)==0 ); for(/*no-op*/; iWritepWith = W; if( p->pPrior ){ + u16 allValues = SF_Values; pNext = 0; for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ pLoop->pNext = pNext; pLoop->selFlags |= SF_Compound; + allValues &= pLoop->selFlags; } - mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; - if( mxSelect && cnt>mxSelect ){ + if( allValues ){ + p->selFlags |= SF_AllValues; + }else if( + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 + && cnt>mxSelect + ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); } } }else{ sqlite3WithDelete(pParse->db, W); Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -56,24 +56,29 @@ u8 sortFlags; /* Zero or more SORTFLAG_* bits */ }; #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ /* -** Delete all the content of a Select structure but do not deallocate -** the select structure itself. +** Delete all the content of a Select structure. Deallocate the structure +** itself only if bFree is true. */ -static void clearSelect(sqlite3 *db, Select *p){ - sqlite3ExprListDelete(db, p->pEList); - sqlite3SrcListDelete(db, p->pSrc); - sqlite3ExprDelete(db, p->pWhere); - sqlite3ExprListDelete(db, p->pGroupBy); - sqlite3ExprDelete(db, p->pHaving); - sqlite3ExprListDelete(db, p->pOrderBy); - sqlite3SelectDelete(db, p->pPrior); - sqlite3ExprDelete(db, p->pLimit); - sqlite3ExprDelete(db, p->pOffset); - sqlite3WithDelete(db, p->pWith); +static void clearSelect(sqlite3 *db, Select *p, int bFree){ + while( p ){ + Select *pPrior = p->pPrior; + sqlite3ExprListDelete(db, p->pEList); + sqlite3SrcListDelete(db, p->pSrc); + sqlite3ExprDelete(db, p->pWhere); + sqlite3ExprListDelete(db, p->pGroupBy); + sqlite3ExprDelete(db, p->pHaving); + sqlite3ExprListDelete(db, p->pOrderBy); + sqlite3ExprDelete(db, p->pLimit); + sqlite3ExprDelete(db, p->pOffset); + sqlite3WithDelete(db, p->pWith); + if( bFree ) sqlite3DbFree(db, p); + p = pPrior; + bFree = 1; + } } /* ** Initialize a SelectDest structure. */ @@ -128,12 +133,11 @@ pNew->pOffset = pOffset; assert( pOffset==0 || pLimit!=0 ); pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; if( db->mallocFailed ) { - clearSelect(db, pNew); - if( pNew!=&standin ) sqlite3DbFree(db, pNew); + clearSelect(db, pNew, pNew!=&standin); pNew = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); } assert( pNew!=&standin ); @@ -154,14 +158,11 @@ /* ** Delete the given Select structure and all of its substructures. */ void sqlite3SelectDelete(sqlite3 *db, Select *p){ - if( p ){ - clearSelect(db, p); - sqlite3DbFree(db, p); - } + clearSelect(db, p, 1); } /* ** Return a pointer to the right-most SELECT statement in a compound. */ @@ -2073,10 +2074,70 @@ Parse *pParse, /* Parsing context */ Select *p, /* The right-most of SELECTs to be coded */ SelectDest *pDest /* What to do with query results */ ); +/* +** Error message for when two or more terms of a compound select have different +** size result sets. +*/ +static void selectWrongNumTermsError(Parse *pParse, Select *p){ + if( p->selFlags & SF_Values ){ + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); + }else{ + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" + " do not have the same number of result columns", selectOpName(p->op)); + } +} + +/* +** Handle the special case of a compound-select that originates from a +** VALUES clause. By handling this as a special case, we avoid deep +** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT +** on a VALUES clause. +** +** Because the Select object originates from a VALUES clause: +** (1) It has no LIMIT or OFFSET +** (2) All terms are UNION ALL +** (3) There is no ORDER BY clause +*/ +static int multiSelectValues( + Parse *pParse, /* Parsing context */ + Select *p, /* The right-most of SELECTs to be coded */ + SelectDest *pDest /* What to do with query results */ +){ + Select *pPrior; + int nExpr = p->pEList->nExpr; + int nRow = 1; + int rc = 0; + assert( p->pNext==0 ); + assert( p->selFlags & SF_AllValues ); + do{ + assert( p->selFlags & SF_Values ); + assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); + assert( p->pLimit==0 ); + assert( p->pOffset==0 ); + if( p->pEList->nExpr!=nExpr ){ + selectWrongNumTermsError(pParse, p); + return 1; + } + if( p->pPrior==0 ) break; + assert( p->pPrior->pNext==p ); + p = p->pPrior; + nRow++; + }while(1); + while( p ){ + pPrior = p->pPrior; + p->pPrior = 0; + rc = sqlite3Select(pParse, p, pDest); + p->pPrior = pPrior; + if( rc ) break; + p->nSelectRow = nRow; + p = p->pNext; + } + return rc; +} /* ** This routine is called to process a compound query form from ** two or more separate queries using UNION, UNION ALL, EXCEPT, or ** INTERSECT @@ -2153,22 +2214,24 @@ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } + + /* Special handling for a compound-select that originates as a VALUES clause. + */ + if( p->selFlags & SF_AllValues ){ + rc = multiSelectValues(pParse, p, &dest); + goto multi_select_end; + } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - if( p->selFlags & SF_Values ){ - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); - }else{ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); - } + selectWrongNumTermsError(pParse, p); rc = 1; goto multi_select_end; } #ifndef SQLITE_OMIT_CTE @@ -4050,11 +4113,13 @@ if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ return WRC_Prune; } pTabList = p->pSrc; pEList = p->pEList; - sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + if( pWalker->xSelectCallback2==selectPopWith ){ + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + } /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. */ sqlite3SrcListAssignCursors(pParse, pTabList); @@ -4341,11 +4406,13 @@ if( pParse->hasCompound ){ w.xSelectCallback = convertCompoundSelectToSubquery; sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + if( (pSelect->selFlags & SF_AllValues)==0 ){ + w.xSelectCallback2 = selectPopWith; + } sqlite3WalkSelect(&w, pSelect); } #ifndef SQLITE_OMIT_SUBQUERY Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2357,11 +2357,11 @@ #define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0040 /* Part of a compound query */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ - /* 0x0100 NOT USED */ +#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -259,10 +259,12 @@ /* ** Usage: clang_sanitize_address ** ** Returns true if the program was compiled using clang with the ** -fsanitize=address switch on the command line. False otherwise. +** +** Also return true if the OMIT_MISUSE environment variable exists. */ static int clang_sanitize_address( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -272,10 +274,11 @@ #if defined(__has_feature) # if __has_feature(address_sanitizer) res = 1; # endif #endif + if( res==0 && getenv("OMIT_MISUSE")!=0 ) res = 1; Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } /* @@ -6794,10 +6797,64 @@ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ +/* +** tclcmd: bad_behavior TYPE +** +** Do some things that should trigger a valgrind or -fsanitize=undefined +** warning. This is used to verify that errors and warnings output by those +** tools are detected by the test scripts. +** +** TYPE BEHAVIOR +** 1 Overflow a signed integer +** 2 Jump based on an uninitialized variable +** 3 Read after free +*/ +static int test_bad_behavior( + ClientData clientData, /* Pointer to an integer containing zero */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iType; + int xyz; + int i = *(int*)clientData; + int j; + int w[10]; + int *a; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "TYPE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iType) ) return TCL_ERROR; + switch( iType ){ + case 1: { + xyz = 0x7fffff00 - i; + xyz += 0x100; + Tcl_SetObjResult(interp, Tcl_NewIntObj(xyz)); + break; + } + case 2: { + w[1] = 5; + if( w[i]>0 ) w[1]++; + Tcl_SetObjResult(interp, Tcl_NewIntObj(w[1])); + break; + } + case 3: { + a = malloc( sizeof(int)*10 ); + for(j=0; j<10; j++) a[j] = j; + free(a); + Tcl_SetObjResult(interp, Tcl_NewIntObj(a[i])); + break; + } + } + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; @@ -6810,10 +6867,11 @@ extern int sqlite3_hostid_num; #endif extern int sqlite3_max_blobsize; extern int sqlite3BtreeSharedCacheReport(void*, Tcl_Interp*,int,Tcl_Obj*CONST*); + static int iZero = 0; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "db_enter", (Tcl_CmdProc*)db_enter }, @@ -6862,10 +6920,11 @@ static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { + { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_int64", test_bind_int64, 0 }, { "sqlite3_bind_double", test_bind_double, 0 }, Index: src/threads.c ================================================================== --- src/threads.c +++ src/threads.c @@ -24,10 +24,13 @@ ** This interface exists so that applications that want to take advantage ** of multiple cores can do so, while also allowing applications to stay ** single-threaded if desired. */ #include "sqliteInt.h" +#if SQLITE_OS_WIN +# include "os_win.h" +#endif #if SQLITE_MAX_WORKER_THREADS>0 /********************************* Unix Pthreads ****************************/ #if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -847,11 +847,11 @@ if( !sqlite3TempInMemory(db) ){ u32 szPma = sqlite3GlobalConfig.szPma; pSorter->mnPmaSize = szPma * pgsz; mxCache = db->aDb[0].pSchema->cache_size; - if( mxCachemxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ); /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary ** large heap allocations. Index: test/main.test ================================================================== --- test/main.test +++ test/main.test @@ -512,7 +512,27 @@ set rc [catch {sqlite3 db test.db -vfs async} msg] list $rc $msg } {1 {no such vfs: async}} } } + +# Print the version number so that it can be picked up by releasetest.tcl. +# +puts [db one {SELECT 'VERSION: ' || + sqlite_version() || ' ' || + sqlite_source_id();}] + +# Do deliberate failures if the TEST_FAILURE environment variable is set. +# This is done to verify that failure notifications are detected by the +# releasetest.tcl script, or possibly by other scripts involved in automatic +# testing. +# +if {[info exists ::env(TEST_FAILURE)]} { + set res 123 + if {$::env(TEST_FAILURE)==0} {set res 234} + do_test main-99.1 { + bad_behavior $::env(TEST_FAILURE) + set x 123 + } $res +} finish_test Index: test/oserror.test ================================================================== --- test/oserror.test +++ test/oserror.test @@ -49,23 +49,24 @@ # The xOpen() method of the unix VFS calls getcwd() as well as open(). # Although this does not appear to be documented in the man page, on OSX # a call to getcwd() may fail if there are no free file descriptors. So # an error may be reported for either open() or getcwd() here. # -puts "Possible valgrind error about invalid file descriptor follows:" -do_test 1.1.1 { - set ::log [list] - list [catch { - for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } - } msg] $msg -} {1 {unable to open database file}} -do_test 1.1.2 { - catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } -} {1} -do_re_test 1.1.3 { - lindex $::log 0 -} {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +if {![clang_sanitize_address]} { + do_test 1.1.1 { + set ::log [list] + list [catch { + for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } + } msg] $msg + } {1 {unable to open database file}} + do_test 1.1.2 { + catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } + } {1} + do_re_test 1.1.3 { + lindex $::log 0 + } {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +} # Test a failure in open() due to the path being a directory. # do_test 1.2.1 { Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -121,10 +121,18 @@ if {[info exists ::env(QUICKTEST_OMIT)]} { foreach x [split $::env(QUICKTEST_OMIT) ,] { regsub -all \\y$x\\y $allquicktests {} allquicktests } } + +# If the TEST_FAILURE environment variable is set, it means that we what to +# deliberately provoke test failures in order to test the test infrastructure. +# Only the main.test module is needed for this. +# +if {[info exists ::env(TEST_FAILURE)]} { + set allquicktests main.test +} ############################################################################# # Start of tests # Index: test/releasetest.tcl ================================================================== --- test/releasetest.tcl +++ test/releasetest.tcl @@ -11,10 +11,11 @@ --srcdir TOP-OF-SQLITE-TREE (see below) --platform PLATFORM (see below) --config CONFIGNAME (Run only CONFIGNAME) --quick (Run "veryquick.test" only) + --veryquick (Run "make smoketest" only) --buildonly (Just build testfixture - do not run) --dryrun (Print what would have happened) --info (Show diagnostic info) The default value for --srcdir is the parent of the directory holding @@ -29,15 +30,11 @@ } array set ::Configs { "Default" { -O2 - } - "Ftrapv" { - -O2 -ftrapv - -DSQLITE_MAX_ATTACHED=125 - -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + --disable-amalgamation --disable-shared } "Sanitize" { CC=clang -fsanitize=undefined -DSQLITE_ENABLE_STAT4 } @@ -72,10 +69,11 @@ -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 } "Debug-One" { + --disable-shared -O2 -DSQLITE_DEBUG=1 -DSQLITE_MEMDEBUG=1 -DSQLITE_MUTEX_NOOP=1 -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 @@ -83,10 +81,11 @@ -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_MEMSYS3=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_STAT4 + -DSQLITE_MAX_ATTACHED=125 } "Device-One" { -O2 -DSQLITE_DEBUG=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 @@ -153,16 +152,23 @@ -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_PARENTHESIS -DSQLITE_DISABLE_FTS4_DEFERRED -DSQLITE_ENABLE_RTREE } - "No-lookaside" { -DSQLITE_TEST_REALLOC_STRESS=1 -DSQLITE_OMIT_LOOKASIDE=1 -DHAVE_USLEEP=1 } + "Valgrind" { + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_RTREE + } + Fail0 {-O0} + Fail2 {-O0} + Fail3 {-O0} } array set ::Platforms { Linux-x86_64 { "Check-Symbols" checksymbols @@ -170,14 +176,14 @@ "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test "Extra-Robustness" test "Device-Two" test - "Ftrapv" test - "Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test} "No-lookaside" test "Devkit" test + "Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test} + "Valgrind" valgrindtest "Default" "threadtest fulltest" "Device-One" fulltest } Linux-i686 { "Devkit" test @@ -195,10 +201,16 @@ "OS-X" "threadtest fulltest" } "Windows NT-intel" { "Default" "mptest fulltestonly" } + Failure-Detection { + Fail0 "TEST_FAILURE=0 test" + Sanitize "TEST_FAILURE=1 test" + Fail2 "TEST_FAILURE=2 valgrindtest" + Fail3 "TEST_FAILURE=3 valgrindtest" + } } # End of configuration section. ######################################################################### @@ -241,10 +253,26 @@ if {$rc==0} { set rc 1 set errmsg $msg } } + if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} { + incr ::NERRCASE + if {$rc==0} { + set rc 1 + set errmsg $all + } + } + if {[regexp {^VERSION: 3\.\d+.\d+} $line]} { + set v [string range $line 9 end] + if {$::SQLITE_VERSION eq ""} { + set ::SQLITE_VERSION $v + } elseif {$::SQLITE_VERSION ne $v} { + set rc 1 + set errmsg "version conflict: {$::SQLITE_VERSION} vs. {$v}" + } + } } close $fd if {!$seen} { set rc 1 set errmsg "Test did not complete" @@ -257,16 +285,21 @@ # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but # CFLAGS is only passed to gcc. # set cflags "-g" set opts "" + set title ${name}($testtarget) + set configOpts "" + regsub -all {#[^\n]*\n} $config \n config foreach arg $config { if {[string match -D* $arg]} { lappend opts $arg } elseif {[string match CC=* $arg]} { lappend testtarget $arg + } elseif {[regexp {^--(enable|disable)-} $arg]} { + lappend configOpts $arg } else { lappend cflags $arg } } @@ -282,31 +315,31 @@ append opts " -DSQLITE_OS_WIN=1" } else { append opts " -DSQLITE_OS_UNIX=1" } - dryrun file mkdir $dir - if {!$::DRYRUN} { - set title ${name}($testtarget) + if {!$::TRACE} { set n [string length $title] puts -nonewline "${title}[string repeat . [expr {63-$n}]]" flush stdout } + set rc 0 set tm1 [clock seconds] set origdir [pwd] - dryrun cd $dir + trace_cmd file mkdir $dir + trace_cmd cd $dir set errmsg {} - set rc [catch [configureCommand]] + set rc [catch [configureCommand $configOpts]] if {!$rc} { set rc [catch [makeCommand $testtarget $cflags $opts]] count_tests_and_errors test.log rc errmsg } + trace_cmd cd $origdir set tm2 [clock seconds] - dryrun cd $origdir - if {!$::DRYRUN} { + if {!$::TRACE} { set hours [expr {($tm2-$tm1)/3600}] set minutes [expr {(($tm2-$tm1)/60)%60}] set seconds [expr {($tm2-$tm1)%60}] set tm [format (%02d:%02d:%02d) $hours $minutes $seconds] if {$rc} { @@ -320,37 +353,40 @@ } # The following procedure returns the "configure" command to be exectued for # the current platform, which may be Windows (via MinGW, etc). # -proc configureCommand {} { - set result [list dryrun exec] +proc configureCommand {opts} { + set result [list trace_cmd exec] if {$::tcl_platform(platform)=="windows"} { lappend result sh } - lappend result $::SRCDIR/configure -enable-load-extension >& test.log + lappend result $::SRCDIR/configure --enable-load-extension + foreach x $opts {lappend result $x} + lappend result >& test.log } # The following procedure returns the "make" command to be executed for the # specified targets, compiler flags, and options. # proc makeCommand { targets cflags opts } { - set result [list dryrun exec make clean] + set result [list trace_cmd exec make clean] foreach target $targets { lappend result $target } lappend result CFLAGS=$cflags OPTS=$opts >>& test.log } -# The following procedure either prints its arguments (if ::DRYRUN is true) -# or executes the command of its arguments in the calling context -# (if ::DRYRUN is false). -# -proc dryrun {args} { - if {$::DRYRUN} { - puts $args - } else { +# The following procedure prints its arguments if ::TRACE is true. +# And it executes the command of its arguments in the calling context +# if ::DRYRUN is false. +# +proc trace_cmd {args} { + if {$::TRACE} { + puts $args + } + if {!$::DRYRUN} { uplevel 1 $args } } @@ -363,17 +399,18 @@ set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]] set ::QUICK 0 set ::BUILDONLY 0 set ::DRYRUN 0 set ::EXEC exec + set ::TRACE 0 set config {} set platform $::tcl_platform(os)-$::tcl_platform(machine) for {set i 0} {$i < [llength $argv]} {incr i} { set x [lindex $argv $i] if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]} - switch -- $x { + switch -glob -- $x { -srcdir { incr i set ::SRCDIR [file normalize [lindex $argv $i]] } @@ -383,10 +420,13 @@ } -quick { set ::QUICK 1 } + -veryquick { + set ::QUICK 2 + } -config { incr i set config [lindex $argv $i] } @@ -396,19 +436,24 @@ } -dryrun { set ::DRYRUN 1 } + + -trace { + set ::TRACE 1 + } -info { puts "Command-line Options:" puts " --srcdir $::SRCDIR" puts " --platform [list $platform]" puts " --config [list $config]" if {$::QUICK} {puts " --quick"} if {$::BUILDONLY} {puts " --buildonly"} if {$::DRYRUN} {puts " --dryrun"} + if {$::TRACE} {puts " --trace"} puts "\nAvailable --platform options:" foreach y [lsort [array names ::Platforms]] { puts " [list $y]" } puts "\nAvailable --config options:" @@ -415,10 +460,18 @@ foreach y [lsort [array names ::Configs]] { puts " [list $y]" } exit } + -g - + -D* - + -O* - + -enable-* - + -disable-* - + *=* { + lappend ::EXTRACONFIG [lindex $argv $i] + } default { puts stderr "" puts stderr [string trim $::USAGE_MESSAGE] exit -1 @@ -447,39 +500,50 @@ puts "Running the following test configurations for $platform:" puts " [string trim $::CONFIGLIST]" puts -nonewline "Flags:" if {$::DRYRUN} {puts -nonewline " --dryrun"} if {$::BUILDONLY} {puts -nonewline " --buildonly"} - if {$::QUICK} {puts -nonewline " --quick"} + switch -- $::QUICK { + 1 {puts -nonewline " --quick"} + 2 {puts -nonewline " --veryquick"} + } puts "" } # Main routine. # proc main {argv} { # Process any command line options. + set ::EXTRACONFIG {} process_options $argv puts [string repeat * 79] set ::NERR 0 set ::NTEST 0 set ::NTESTCASE 0 set ::NERRCASE 0 + set ::SQLITE_VERSION {} set STARTTIME [clock seconds] foreach {zConfig target} $::CONFIGLIST { - if {$::QUICK} {set target test} - if {$::BUILDONLY} {set target testfixture} - set config_options $::Configs($zConfig) + if {$target ne "checksymbols"} { + switch -- $::QUICK { + 1 {set target test} + 2 {set target smoketest} + } + if {$::BUILDONLY} {set target testfixture} + } + set config_options [concat $::Configs($zConfig) $::EXTRACONFIG] incr NTEST run_test_suite $zConfig $target $config_options # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. - if {$target!="checksymbols" && !$::BUILDONLY} { + if {$target!="checksymbols" && $target!="valgrindtest" + && !$::BUILDONLY && $::QUICK<2} { set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] set xtarget $target regsub -all {fulltest[a-z]*} $xtarget test xtarget if {$debug_idx < 0} { incr NTEST @@ -498,9 +562,12 @@ set hr [expr {$elapsetime/3600}] set min [expr {($elapsetime/60)%60}] set sec [expr {$elapsetime%60}] set etime [format (%02d:%02d:%02d) $hr $min $sec] puts [string repeat * 79] - puts "$::NERRCASE failures of $::NTESTCASE tests run in $etime" + puts "$::NERRCASE failures out of $::NTESTCASE tests in $etime" + if {$::SQLITE_VERSION ne ""} { + puts "SQLite $::SQLITE_VERSION" + } } main $argv ADDED test/selectG.test Index: test/selectG.test ================================================================== --- /dev/null +++ test/selectG.test @@ -0,0 +1,39 @@ +# 2015-01-05 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file verifies that INSERT operations with a very large number of +# VALUE terms works and does not hit the SQLITE_LIMIT_COMPOUND_SELECT limit. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix selectG + +# Do an INSERT with a VALUES clause that contains 100,000 entries. Verify +# that this insert happens quickly (in less than 10 seconds). Actually, the +# insert will normally happen in less than 0.5 seconds on a workstation, but +# we allow plenty of overhead for slower machines. The speed test checks +# for an O(N*N) inefficiency that was once in the code and that would make +# the insert run for over a minute. +# +do_test 100 { + set sql "CREATE TABLE t1(x);\nINSERT INTO t1(x) VALUES" + for {set i 1} {$i<100000} {incr i} { + append sql "($i)," + } + append sql "($i);" + set microsec [lindex [time {db eval $sql}] 0] + db eval { + SELECT count(x), sum(x), avg(x), $microsec<10000000 FROM t1; + } +} {100000 5000050000 50000.5 1} + +finish_test