diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/analyze9.test | 3 | ||||
-rw-r--r-- | test/atof1.test | 2 | ||||
-rw-r--r-- | test/autoinc.test | 2 | ||||
-rw-r--r-- | test/default.test | 35 | ||||
-rw-r--r-- | test/e_expr.test | 10 | ||||
-rw-r--r-- | test/func.test | 18 | ||||
-rw-r--r-- | test/func5.test | 33 | ||||
-rw-r--r-- | test/misc7.test | 2 | ||||
-rw-r--r-- | test/run-wordcount.sh | 78 | ||||
-rw-r--r-- | test/speedtest1.c | 951 | ||||
-rw-r--r-- | test/win32heap.test | 74 | ||||
-rw-r--r-- | test/wordcount.c | 76 |
12 files changed, 1268 insertions, 16 deletions
diff --git a/test/analyze9.test b/test/analyze9.test index aaf0ba3bb..0fce55ac4 100644 --- a/test/analyze9.test +++ b/test/analyze9.test @@ -805,8 +805,9 @@ do_test 16.1 { ANALYZE; } set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] + puts -nonewline " (nByte=$nByte nByte2=$nByte2)" - expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1050} + expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1100} } {1} #------------------------------------------------------------------------- diff --git a/test/atof1.test b/test/atof1.test index 76eb4273b..5c04d0229 100644 --- a/test/atof1.test +++ b/test/atof1.test @@ -15,7 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -if {![info exists __GNUC__]} { +if {![info exists __GNUC__] || [regexp arm $tcl_platform(machine)]} { finish_test return } diff --git a/test/autoinc.test b/test/autoinc.test index 98f6919b0..239600616 100644 --- a/test/autoinc.test +++ b/test/autoinc.test @@ -216,7 +216,7 @@ do_test autoinc-2.27 { } {t1 1238} do_test autoinc-2.28 { execsql { - UPDATE sqlite_sequence SET seq='12345678901234567890' + UPDATE sqlite_sequence SET seq='-12345678901234567890' WHERE name='t1'; INSERT INTO t1 VALUES(NULL,6); SELECT * FROM t1; diff --git a/test/default.test b/test/default.test index 95a4ee039..d6b6f97d9 100644 --- a/test/default.test +++ b/test/default.test @@ -64,4 +64,39 @@ ifcapable pragma { } {0 c {} 0 'abc' 0} } +do_execsql_test default-3.1 { + CREATE TABLE t3( + a INTEGER PRIMARY KEY AUTOINCREMENT, + b INT DEFAULT 12345 UNIQUE NOT NULL CHECK( b>=0 AND b<99999 ), + c VARCHAR(123,456) DEFAULT 'hello' NOT NULL ON CONFLICT REPLACE, + d REAL, + e FLOATING POINT(5,10) DEFAULT 4.36, + f NATIONAL CHARACTER(15) COLLATE RTRIM, + g LONG INTEGER DEFAULT( 3600*12 ) + ); + INSERT INTO t3 VALUES(null, 5, 'row1', '5.25', 'xyz', 321, '432'); + SELECT a, typeof(a), b, typeof(b), c, typeof(c), + d, typeof(d), e, typeof(e), f, typeof(f), + g, typeof(g) FROM t3; +} {1 integer 5 integer row1 text 5.25 real xyz text 321 text 432 integer} +do_execsql_test default-3.2 { + DELETE FROM t3; + INSERT INTO t3 DEFAULT VALUES; + SELECT * FROM t3; +} {2 12345 hello {} 4.36 {} 43200} +do_execsql_test default-3.3 { + CREATE TABLE t300( + a INT DEFAULT 2147483647, + b INT DEFAULT 2147483648, + c INT DEFAULT +9223372036854775807, + d INT DEFAULT -2147483647, + e INT DEFAULT -2147483648, + f INT DEFAULT -9223372036854775808, + g INT DEFAULT (-(-9223372036854775808)), + h INT DEFAULT (-(-9223372036854775807)) + ); + INSERT INTO t300 DEFAULT VALUES; + SELECT * FROM t300; +} {2147483647 2147483648 9223372036854775807 -2147483647 -2147483648 -9223372036854775808 9.22337203685478e+18 9223372036854775807} + finish_test diff --git a/test/e_expr.test b/test/e_expr.test index f0705757a..eead6a2b9 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -1081,9 +1081,9 @@ ifcapable !icu { # EVIDENCE-OF: R-33693-50180 The REGEXP operator is a special syntax for # the regexp() user function. # -# EVIDENCE-OF: R-57289-13578 If a application-defined SQL function named -# "regexp" is added at run-time, that function will be called in order -# to implement the REGEXP operator. +# EVIDENCE-OF: R-65524-61849 If an application-defined SQL function +# named "regexp" is added at run-time, then the "X REGEXP Y" operator +# will be implemented as a call to "regexp(Y,X)". # proc regexpfunc {args} { eval lappend ::regexpargs $args @@ -1606,14 +1606,14 @@ do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0 # an INTEGER then the result of the cast is the largest negative # integer: -9223372036854775808. # -do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808 +do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer 9223372036854775807 do_expr_test e_expr-31.2.2 { CAST(-2e+50 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.3 { CAST(-9223372036854775809.0 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.4 { CAST(9223372036854775809.0 AS INT) -} integer -9223372036854775808 +} integer 9223372036854775807 # EVIDENCE-OF: R-09295-61337 Casting a TEXT or BLOB value into NUMERIC diff --git a/test/func.test b/test/func.test index 7c7d55e1b..edec591ea 100644 --- a/test/func.test +++ b/test/func.test @@ -1319,6 +1319,24 @@ do_test func-29.6 { set x } {1} +# The OP_Column opcode has an optimization that avoids loading content +# for fields with content-length=0 when the content offset is on an overflow +# page. Make sure the optimization works. +# +do_execsql_test func-29.10 { + CREATE TABLE t29b(a,b,c,d,e,f,g,h,i); + INSERT INTO t29b + VALUES(1, hex(randomblob(2000)), null, 0, 1, '', zeroblob(0),'x',x'01'); + SELECT typeof(c), typeof(d), typeof(e), typeof(f), + typeof(g), typeof(h), typeof(i) FROM t29b; +} {null integer integer text blob text blob} +do_execsql_test func-29.11 { + SELECT length(f), length(g), length(h), length(i) FROM t29b; +} {0 0 1 1} +do_execsql_test func-29.12 { + SELECT quote(f), quote(g), quote(h), quote(i) FROM t29b; +} {'' X'' 'x' X'01'} + # EVIDENCE-OF: R-29701-50711 The unicode(X) function returns the numeric # unicode code point corresponding to the first character of the string # X. diff --git a/test/func5.test b/test/func5.test new file mode 100644 index 000000000..83ecb785d --- /dev/null +++ b/test/func5.test @@ -0,0 +1,33 @@ +# 2013-11-21 +# +# 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. +# +#************************************************************************* +# +# Verify that constant string expressions that get factored into initializing +# code are not reused between function parameters and other values in the +# VDBE program, as the function might have changed the encoding. +# +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test func5-1.1 { + PRAGMA encoding=UTF16le; + CREATE TABLE t1(x,a,b,c); + INSERT INTO t1 VALUES(1,'ab','cd',1); + INSERT INTO t1 VALUES(2,'gh','ef',5); + INSERT INTO t1 VALUES(3,'pqr','fuzzy',99); + INSERT INTO t1 VALUES(4,'abcdefg','xy',22); + INSERT INTO t1 VALUES(5,'shoe','mayer',2953); + SELECT x FROM t1 WHERE c=instr('abcdefg',b) OR a='abcdefg' ORDER BY +x; +} {2 4} +do_execsql_test func5-1.2 { + SELECT x FROM t1 WHERE a='abcdefg' OR c=instr('abcdefg',b) ORDER BY +x; +} {2 4} + +finish_test diff --git a/test/misc7.test b/test/misc7.test index 96062a93b..ec042ff0e 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -355,7 +355,7 @@ do_ioerr_test misc7-16 -sqlprep { COMMIT; }} msg] - if {!$rc || ($rc && [string first "columns" $msg]==0)} { + if {!$rc || ($rc && [string first "UNIQUE" $msg]==0)} { set msg } else { error $msg diff --git a/test/run-wordcount.sh b/test/run-wordcount.sh new file mode 100644 index 000000000..1755d5816 --- /dev/null +++ b/test/run-wordcount.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# This script runs the wordcount program in different ways, comparing +# the output from each. +# + +# Select the source text to be analyzed. +# +if test "x$1" = "x"; +then echo "Usage: $0 FILENAME [ARGS...]"; exit 1; +fi + +# Do test runs +# +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --insert >wc-out.txt +mv wc-out.txt wc-baseline.txt +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --insert --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --replace >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --replace --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --select >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --select --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +./wordcount --timer --summary wcdb1.db $* --query >wc-out.txt +mv wc-out.txt wc-baseline.txt +./wordcount --timer --summary wcdb2.db $* --query --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +./wordcount --timer --summary wcdb1.db $* --delete >wc-out.txt +mv wc-out.txt wc-baseline.txt +./wordcount --timer --summary wcdb2.db $* --delete --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + + +# Clean up temporary files created. +# +rm -rf wcdb1.db wcdb2.db wc-out.txt wc-baseline.txt diff --git a/test/speedtest1.c b/test/speedtest1.c new file mode 100644 index 000000000..b15f65cfe --- /dev/null +++ b/test/speedtest1.c @@ -0,0 +1,951 @@ +/* +** A program for performance testing. +** +** The available command-line options are described below: +*/ +static const char zHelp[] = + "Usage: %s [--options] DATABASE\n" + "Options:\n" + " --autovacuum Enable AUTOVACUUM mode\n" + " --cachesize N Set the cache size to N\n" + " --exclusive Enable locking_mode=EXCLUSIVE\n" + " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" + " --incrvacuum Enable incremenatal vacuum mode\n" + " --journalmode M Set the journal_mode to MODE\n" + " --key KEY Set the encryption key to KEY\n" + " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n" + " --nosync Set PRAGMA synchronous=OFF\n" + " --notnull Add NOT NULL constraints to table columns\n" + " --pagesize N Set the page size to N\n" + " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n" + " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n" + " --reprepare Reprepare each statement upon every invocation\n" + " --scratch N SZ Configure scratch memory for N slots of SZ bytes each\n" + " --sqlonly No-op. Only show the SQL that would have been run.\n" + " --size N Relative test size. Default=100\n" + " --stats Show statistics at the end\n" + " --testset T Run test-set T\n" + " --trace Turn on SQL tracing\n" + " --utf16be Set text encoding to UTF-16BE\n" + " --utf16le Set text encoding to UTF-16LE\n" + " --without-rowid Use WITHOUT ROWID where appropriate\n" +; + + +#include "sqlite3.h" +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +/* All global state is held in this structure */ +static struct Global { + sqlite3 *db; /* The open database connection */ + sqlite3_stmt *pStmt; /* Current SQL statement */ + sqlite3_int64 iStart; /* Start-time for the current test */ + sqlite3_int64 iTotal; /* Total time */ + int bWithoutRowid; /* True for --without-rowid */ + int bReprepare; /* True to reprepare the SQL on each rerun */ + int bSqlOnly; /* True to print the SQL once only */ + int szTest; /* Scale factor for test iterations */ + const char *zWR; /* Might be WITHOUT ROWID */ + const char *zNN; /* Might be NOT NULL */ + const char *zPK; /* Might be UNIQUE or PRIMARY KEY */ + unsigned int x, y; /* Pseudo-random number generator state */ + int nResult; /* Size of the current result */ + char zResult[3000]; /* Text of the current result */ +} g; + + +/* Print an error message and exit */ +static void fatal_error(const char *zMsg, ...){ + va_list ap; + va_start(ap, zMsg); + vfprintf(stderr, zMsg, ap); + va_end(ap); + exit(1); +} + +/* +** Return the value of a hexadecimal digit. Return -1 if the input +** is not a hex digit. +*/ +static int hexDigitValue(char c){ + if( c>='0' && c<='9' ) return c - '0'; + if( c>='a' && c<='f' ) return c - 'a' + 10; + if( c>='A' && c<='F' ) return c - 'A' + 10; + return -1; +} + +/* +** Interpret zArg as an integer value, possibly with suffixes. +*/ +static int integerValue(const char *zArg){ + sqlite3_int64 v = 0; + static const struct { char *zSuffix; int iMult; } aMult[] = { + { "KiB", 1024 }, + { "MiB", 1024*1024 }, + { "GiB", 1024*1024*1024 }, + { "KB", 1000 }, + { "MB", 1000000 }, + { "GB", 1000000000 }, + { "K", 1000 }, + { "M", 1000000 }, + { "G", 1000000000 }, + }; + int i; + int isNeg = 0; + if( zArg[0]=='-' ){ + isNeg = 1; + zArg++; + }else if( zArg[0]=='+' ){ + zArg++; + } + if( zArg[0]=='0' && zArg[1]=='x' ){ + int x; + zArg += 2; + while( (x = hexDigitValue(zArg[0]))>=0 ){ + v = (v<<4) + x; + zArg++; + } + }else{ + while( isdigit(zArg[0]) ){ + v = v*10 + zArg[0] - '0'; + zArg++; + } + } + for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){ + if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ + v *= aMult[i].iMult; + break; + } + } + if( v>=2147483648 ) fatal_error("parameter to large - max 2147483648"); + return isNeg? -v : v; +} + +/* Return the current wall-clock time, in milliseconds */ +sqlite3_int64 speedtest1_timestamp(void){ + static sqlite3_vfs *clockVfs = 0; + sqlite3_int64 t; + if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ + clockVfs->xCurrentTimeInt64(clockVfs, &t); + }else{ + double r; + clockVfs->xCurrentTime(clockVfs, &r); + t = (sqlite3_int64)(r*86400000.0); + } + return t; +} + +/* Return a pseudo-random unsigned integer */ +unsigned int speedtest1_random(void){ + g.x = (g.x>>1) ^ ((1+~(g.x&1)) & 0xd0000001); + g.y = g.y*1103515245 + 12345; + return g.x ^ g.y; +} + +/* Map the value in within the range of 1...limit into another +** number in a way that is chatic and invertable. +*/ +unsigned swizzle(unsigned in, unsigned limit){ + unsigned out = 0; + while( limit ){ + out = (out<<1) | (in&1); + in >>= 1; + limit >>= 1; + } + return out; +} + +/* Round up a number so that it is a power of two minus one +*/ +unsigned roundup_allones(unsigned limit){ + unsigned m = 1; + while( m<limit ) m = (m<<1)+1; + return m; +} + +/* The speedtest1_numbername procedure below converts its argment (an integer) +** into a string which is the English-language name for that number. +** The returned string should be freed with sqlite3_free(). +** +** Example: +** +** speedtest1_numbername(123) -> "one hundred twenty three" +*/ +int speedtest1_numbername(unsigned int n, char *zOut, int nOut){ + static const char *ones[] = { "zero", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven", "twelve", + "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", + "eighteen", "nineteen" }; + static const char *tens[] = { "", "ten", "twenty", "thirty", "forty", + "fifty", "sixty", "seventy", "eighty", "ninety" }; + int i = 0; + + if( n>=1000000000 ){ + i += speedtest1_numbername(n/1000000000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " billion"); + i += (int)strlen(zOut+i); + n = n % 1000000000; + } + if( n>=1000000 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + i += speedtest1_numbername(n/1000000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " million"); + i += (int)strlen(zOut+i); + n = n % 1000000; + } + if( n>=1000 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + i += speedtest1_numbername(n/1000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " thousand"); + i += (int)strlen(zOut+i); + n = n % 1000; + } + if( n>=100 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s hundred", ones[n/100]); + i += (int)strlen(zOut+i); + n = n % 100; + } + if( n>=20 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s", tens[n/10]); + i += (int)strlen(zOut+i); + n = n % 10; + } + if( n>0 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s", ones[n]); + i += (int)strlen(zOut+i); + } + if( i==0 ){ + sqlite3_snprintf(nOut-i, zOut+i, "zero"); + i += (int)strlen(zOut+i); + } + return i; +} + + +/* Start a new test case */ +#define NAMEWIDTH 60 +static const char zDots[] = + "......................................................................."; +void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){ + int n = (int)strlen(zTestName); + char *zName; + va_list ap; + va_start(ap, zTestName); + zName = sqlite3_vmprintf(zTestName, ap); + va_end(ap); + n = (int)strlen(zName); + if( n>NAMEWIDTH ){ + zName[NAMEWIDTH] = 0; + n = NAMEWIDTH; + } + if( g.bSqlOnly ){ + printf("/* %4d - %s%.*s */\n", iTestNum, zName, NAMEWIDTH-n, zDots); + }else{ + printf("%4d - %s%.*s ", iTestNum, zName, NAMEWIDTH-n, zDots); + fflush(stdout); + } + sqlite3_free(zName); + g.nResult = 0; + g.iStart = speedtest1_timestamp(); + g.x = 2903710987; + g.y = 1157229256; +} + +/* Complete a test case */ +void speedtest1_end_test(void){ + sqlite3_int64 iElapseTime = speedtest1_timestamp() - g.iStart; + if( !g.bSqlOnly ){ + g.iTotal += iElapseTime; + printf("%4d.%03ds\n", (int)(iElapseTime/1000), (int)(iElapseTime%1000)); + } + if( g.pStmt ){ + sqlite3_finalize(g.pStmt); + g.pStmt = 0; + } +} + +/* Report end of testing */ +void speedtest1_final(void){ + if( !g.bSqlOnly ){ + printf(" TOTAL%.*s %4d.%03ds\n", NAMEWIDTH-5, zDots, + (int)(g.iTotal/1000), (int)(g.iTotal%1000)); + } +} + +/* Run SQL */ +void speedtest1_exec(const char *zFormat, ...){ + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( g.bSqlOnly ){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ){ n--; } + printf("%.*s;\n", n, zSql); + }else{ + char *zErrMsg = 0; + int rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg); + if( zErrMsg ) fatal_error("SQL error: %s\n%s\n", zErrMsg, zSql); + if( rc!=SQLITE_OK ) fatal_error("exec error: %s\n", sqlite3_errmsg(g.db)); + } + sqlite3_free(zSql); +} + +/* Prepare an SQL statement */ +void speedtest1_prepare(const char *zFormat, ...){ + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( g.bSqlOnly ){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ){ n--; } + printf("%.*s;\n", n, zSql); + }else{ + int rc; + if( g.pStmt ) sqlite3_finalize(g.pStmt); + rc = sqlite3_prepare_v2(g.db, zSql, -1, &g.pStmt, 0); + if( rc ){ + fatal_error("SQL error: %s\n", sqlite3_errmsg(g.db)); + } + } + sqlite3_free(zSql); +} + +/* Run an SQL statement previously prepared */ +void speedtest1_run(void){ + int i, n, len; + if( g.bSqlOnly ) return; + assert( g.pStmt ); + g.nResult = 0; + while( sqlite3_step(g.pStmt)==SQLITE_ROW ){ + n = sqlite3_column_count(g.pStmt); + for(i=0; i<n; i++){ + const char *z = (const char*)sqlite3_column_text(g.pStmt, i); + if( z==0 ) z = "nil"; + len = (int)strlen(z); + if( g.nResult+len<sizeof(g.zResult)-2 ){ + if( g.nResult>0 ) g.zResult[g.nResult++] = ' '; + memcpy(g.zResult + g.nResult, z, len+1); + g.nResult += len; + } + } + } + if( g.bReprepare ){ + sqlite3_stmt *pNew; + sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0); + sqlite3_finalize(g.pStmt); + g.pStmt = pNew; + }else{ + sqlite3_reset(g.pStmt); + } +} + +/* The sqlite3_trace() callback function */ +static void traceCallback(void *NotUsed, const char *zSql){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ) n--; + fprintf(stderr,"%.*s;\n", n, zSql); +} + +/* Substitute random() function that gives the same random +** sequence on each run, for repeatability. */ +static void randomFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **NotUsed2 +){ + sqlite3_result_int64(context, (sqlite3_int64)speedtest1_random()); +} + +/* +** The main and default testset +*/ +void testset_main(void){ + int i; /* Loop counter */ + int n; /* iteration count */ + int sz; /* Size of the tables */ + int maxb; /* Maximum swizzled value */ + unsigned x1, x2; /* Parameters */ + int len; /* Length of the zNum[] string */ + char zNum[2000]; /* A number name */ + + sz = n = g.szTest*500; + maxb = roundup_allones(sz); + speedtest1_begin_test(100, "%d INSERTs into table with no index", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t1(a INTEGER %s, b INTEGER %s, c TEXT %s);", + g.zNN, g.zNN, g.zNN); + speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); + sqlite3_bind_int(g.pStmt, 2, i); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(110, "%d ordered INSERTS with one index/PK", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t2(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_prepare("INSERT INTO t2 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, i); + sqlite3_bind_int64(g.pStmt, 2, (sqlite3_int64)x1); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(120, "%d unordered INSERTS with one index/PK", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t3(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_prepare("INSERT INTO t3 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 2, i); + sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = g.szTest/2; + speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = g.szTest/5; + speedtest1_begin_test(140, "%d SELECTS, LIKE, unindexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE c LIKE ?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + zNum[0] = '%'; + len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); + zNum[len] = '%'; + zNum[len+1] = 0; + sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + speedtest1_begin_test(150, "CREATE INDEX five times"); + speedtest1_exec( + "BEGIN;\n" + "CREATE UNIQUE INDEX t1b ON t1(b);\n" + "CREATE INDEX t1c ON t1(c);\n" + "CREATE UNIQUE INDEX t2b ON t2(b);\n" + "CREATE INDEX t2c ON t2(c DESC);\n" + "CREATE INDEX t3bc ON t3(b,c);\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(161, "%d SELECTS, numeric BETWEEN, PK", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t2\n" + " WHERE a BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(170, "%d SELECTS, text BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE c BETWEEN ?1 AND (?1||'~'); -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = swizzle(i, maxb); + len = speedtest1_numbername(x1, zNum, sizeof(zNum)-1); + sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + n = sz; + speedtest1_begin_test(180, "%d INSERTS with three indexes", n); + speedtest1_exec("BEGIN"); + speedtest1_exec( + "CREATE TABLE t4(\n" + " a INTEGER %s %s,\n" + " b INTEGER %s,\n" + " c TEXT %s\n" + ") %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_exec("CREATE INDEX t4b ON t4(b)"); + speedtest1_exec("CREATE INDEX t4c ON t4(c)"); + speedtest1_exec("INSERT INTO t4 SELECT * FROM t1"); + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + n = sz; + speedtest1_begin_test(190, "DELETE and REFILL one table", n); + speedtest1_exec( + "DELETE FROM t2;" + "INSERT INTO t2 SELECT * FROM t1;" + ); + speedtest1_end_test(); + + + speedtest1_begin_test(200, "VACUUM"); + speedtest1_exec("VACUUM"); + speedtest1_end_test(); + + + speedtest1_begin_test(210, "ALTER TABLE ADD COLUMN, and query"); + speedtest1_exec("ALTER TABLE t2 ADD COLUMN d DEFAULT 123"); + speedtest1_exec("SELECT sum(d) FROM t2"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(230, "%d UPDATES, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "UPDATE t2 SET d=b*2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(240, "%d UPDATES of individual rows", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "UPDATE t2 SET d=b*3 WHERE a=?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + sqlite3_bind_int(g.pStmt, 1, x1); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + speedtest1_begin_test(250, "One big UPDATE of the whole %d-row table", sz); + speedtest1_exec("UPDATE t2 SET d=b*4"); + speedtest1_end_test(); + + + speedtest1_begin_test(260, "Query added column after filling"); + speedtest1_exec("SELECT sum(d) FROM t2"); + speedtest1_end_test(); + + + + n = sz/5; + speedtest1_begin_test(270, "%d DELETEs, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "DELETE FROM t2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb + 1; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(280, "%d DELETEs of individual rows", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "DELETE FROM t3 WHERE a=?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + sqlite3_bind_int(g.pStmt, 1, x1); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + speedtest1_begin_test(290, "Refill two %d-row tables using REPLACE", sz); + speedtest1_exec("REPLACE INTO t2(a,b,c) SELECT a,b,c FROM t1"); + speedtest1_exec("REPLACE INTO t3(a,b,c) SELECT a,b,c FROM t1"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(290, "%d four-ways joins", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT t1.c FROM t1, t2, t3, t4\n" + " WHERE t4.a BETWEEN ?1 AND ?2\n" + " AND t3.a=t4.b\n" + " AND t2.a=t3.b\n" + " AND t1.c=t2.c" + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + x2 = speedtest1_random()%10 + x1 + 4; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + + speedtest1_begin_test(980, "PRAGMA integrity_check"); + speedtest1_exec("PRAGMA integrity_check"); + speedtest1_end_test(); + + + speedtest1_begin_test(990, "ANALYZE"); + speedtest1_exec("ANALYZE"); + speedtest1_end_test(); +} + +/* +** A testset used for debugging speedtest1 itself. +*/ +void testset_debug1(void){ + unsigned i, n; + unsigned x1, x2; + char zNum[2000]; /* A number name */ + + n = g.szTest; + for(i=1; i<=n; i++){ + x1 = swizzle(i, n); + x2 = swizzle(x1, n); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + printf("%5d %5d %5d %s\n", i, x1, x2, zNum); + } +} + +int main(int argc, char **argv){ + int doAutovac = 0; /* True for --autovacuum */ + int cacheSize = 0; /* Desired cache size. 0 means default */ + int doExclusive = 0; /* True for --exclusive */ + int nHeap = 0, mnHeap = 0; /* Heap size from --heap */ + int doIncrvac = 0; /* True for --incrvacuum */ + const char *zJMode = 0; /* Journal mode */ + const char *zKey = 0; /* Encryption key */ + int nLook = 0, szLook = 0; /* --lookaside configuration */ + int noSync = 0; /* True for --nosync */ + int pageSize = 0; /* Desired page size. 0 means default */ + int nPCache = 0, szPCache = 0;/* --pcache configuration */ + int nScratch = 0, szScratch=0;/* --scratch configuration */ + int showStats = 0; /* True for --stats */ + const char *zTSet = "main"; /* Which --testset torun */ + int doTrace = 0; /* True for --trace */ + const char *zEncoding = 0; /* --utf16be or --utf16le */ + const char *zDbName = 0; /* Name of the test database */ + + void *pHeap = 0; /* Allocated heap space */ + void *pLook = 0; /* Allocated lookaside space */ + void *pPCache = 0; /* Allocated storage for pcache */ + void *pScratch = 0; /* Allocated storage for scratch */ + int iCur, iHi; /* Stats values, current and "highwater" */ + int i; /* Loop counter */ + int rc; /* API return code */ + + /* Process command-line arguments */ + g.zWR = ""; + g.zNN = ""; + g.zPK = "UNIQUE"; + g.szTest = 100; + for(i=1; i<argc; i++){ + const char *z = argv[i]; + if( z[0]=='-' ){ + do{ z++; }while( z[0]=='-' ); + if( strcmp(z,"autovacuum")==0 ){ + doAutovac = 1; + }else if( strcmp(z,"cachesize")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + i++; + cacheSize = integerValue(argv[i]); + }else if( strcmp(z,"exclusive")==0 ){ + doExclusive = 1; + }else if( strcmp(z,"heap")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nHeap = integerValue(argv[i+1]); + mnHeap = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"incrvacuum")==0 ){ + doIncrvac = 1; + }else if( strcmp(z,"journal")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zJMode = argv[++i]; + }else if( strcmp(z,"key")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zKey = argv[++i]; + }else if( strcmp(z,"lookaside")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nLook = integerValue(argv[i+1]); + szLook = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"nosync")==0 ){ + noSync = 1; + }else if( strcmp(z,"notnull")==0 ){ + g.zNN = "NOT NULL"; + }else if( strcmp(z,"pagesize")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + pageSize = integerValue(argv[++i]); + }else if( strcmp(z,"pcache")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nPCache = integerValue(argv[i+1]); + szPCache = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"primarykey")==0 ){ + g.zPK = "PRIMARY KEY"; + }else if( strcmp(z,"reprepare")==0 ){ + g.bReprepare = 1; + }else if( strcmp(z,"scratch")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nScratch = integerValue(argv[i+1]); + szScratch = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"sqlonly")==0 ){ + g.bSqlOnly = 1; + }else if( strcmp(z,"size")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + g.szTest = integerValue(argv[++i]); + }else if( strcmp(z,"stats")==0 ){ + showStats = 1; + }else if( strcmp(z,"testset")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zTSet = argv[++i]; + }else if( strcmp(z,"trace")==0 ){ + doTrace = 1; + }else if( strcmp(z,"utf16le")==0 ){ + zEncoding = "utf16le"; + }else if( strcmp(z,"utf16be")==0 ){ + zEncoding = "utf16be"; + }else if( strcmp(z,"without-rowid")==0 ){ + g.zWR = "WITHOUT ROWID"; + g.zPK = "PRIMARY KEY"; + }else if( strcmp(z, "help")==0 || strcmp(z,"?")==0 ){ + printf(zHelp, argv[0]); + exit(0); + }else{ + fatal_error("unknown option: %s\nUse \"%s -?\" for help\n", + argv[i], argv[0]); + } + }else if( zDbName==0 ){ + zDbName = argv[i]; + }else{ + fatal_error("surplus argument: %s\nUse \"%s -?\" for help\n", + argv[i], argv[0]); + } + } +#if 0 + if( zDbName==0 ){ + fatal_error(zHelp, argv[0]); + } +#endif + if( nHeap>0 ){ + pHeap = malloc( nHeap ); + if( pHeap==0 ) fatal_error("cannot allocate %d-byte heap\n", nHeap); + rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, mnHeap); + if( rc ) fatal_error("heap configuration failed: %d\n", rc); + } + if( nPCache>0 && szPCache>0 ){ + pPCache = malloc( nPCache*(sqlite3_int64)szPCache ); + if( pPCache==0 ) fatal_error("cannot allocate %lld-byte pcache\n", + nPCache*(sqlite3_int64)szPCache); + rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pPCache, szPCache, nPCache); + if( rc ) fatal_error("pcache configuration failed: %d\n", rc); + } + if( nScratch>0 && szScratch>0 ){ + pScratch = malloc( nScratch*(sqlite3_int64)szScratch ); + if( pScratch==0 ) fatal_error("cannot allocate %lld-byte scratch\n", + nScratch*(sqlite3_int64)szScratch); + rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, pScratch, szScratch, nScratch); + if( rc ) fatal_error("scratch configuration failed: %d\n", rc); + } + if( nLook>0 ){ + sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); + } + + /* Open the database and the input file */ + if( sqlite3_open(zDbName, &g.db) ){ + fatal_error("Cannot open database file: %s\n", zDbName); + } + if( nLook>0 && szLook>0 ){ + pLook = malloc( nLook*szLook ); + rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE, pLook, szLook,nLook); + if( rc ) fatal_error("lookaside configuration failed: %d\n", rc); + } + + /* Set database connection options */ + sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0); + if( doTrace ) sqlite3_trace(g.db, traceCallback, 0); + if( zKey ){ + speedtest1_exec("PRAGMA key('%s')", zKey); + } + if( zEncoding ){ + speedtest1_exec("PRAGMA encoding=%s", zEncoding); + } + if( doAutovac ){ + speedtest1_exec("PRAGMA auto_vacuum=FULL"); + }else if( doIncrvac ){ + speedtest1_exec("PRAGMA auto_vacuum=INCREMENTAL"); + } + if( pageSize ){ + speedtest1_exec("PRAGMA page_size=%d", pageSize); + } + if( cacheSize ){ + speedtest1_exec("PRAGMA cache_size=%d", cacheSize); + } + if( noSync ) speedtest1_exec("PRAGMA synchronous=OFF"); + if( doExclusive ){ + speedtest1_exec("PRAGMA locking_mode=EXCLUSIVE"); + } + if( zJMode ){ + speedtest1_exec("PRAGMA journal_mode=%s", zJMode); + } + + if( strcmp(zTSet,"main")==0 ){ + testset_main(); + }else if( strcmp(zTSet,"debug1")==0 ){ + testset_debug1(); + }else{ + fatal_error("unknown testset: \"%s\"\n", zTSet); + } + speedtest1_final(); + + /* Database connection statistics printed after both prepared statements + ** have been finalized */ + if( showStats ){ + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHi, 0); + printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHi, 0); + printf("-- Successful lookasides: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHi,0); + printf("-- Lookaside size faults: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHi,0); + printf("-- Lookaside OOM faults: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHi, 0); + printf("-- Pager Heap Usage: %d bytes\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHi, 1); + printf("-- Page cache hits: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHi, 1); + printf("-- Page cache misses: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHi, 1); + printf("-- Page cache writes: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHi, 0); + printf("-- Schema Heap Usage: %d bytes\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHi, 0); + printf("-- Statement Heap Usage: %d bytes\n", iCur); + } + + sqlite3_close(g.db); + + /* Global memory usage statistics printed after the database connection + ** has closed. Memory usage should be zero at this point. */ + if( showStats ){ + sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHi, 0); + printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0); + printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0); + printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHi, 0); + printf("-- Scratch Overflow Bytes: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0); + printf("-- Largest Allocation: %d bytes\n",iHi); + sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0); + printf("-- Largest Pcache Allocation: %d bytes\n",iHi); + sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHi, 0); + printf("-- Largest Scratch Allocation: %d bytes\n", iHi); + } + + /* Release memory */ + free( pLook ); + free( pPCache ); + free( pScratch ); + free( pHeap ); + return 0; +} diff --git a/test/win32heap.test b/test/win32heap.test new file mode 100644 index 000000000..b92f8040e --- /dev/null +++ b/test/win32heap.test @@ -0,0 +1,74 @@ +# 2013 November 22 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is recovery from transient manditory locks +# that sometimes appear on database files due to anti-virus software. +# + +if {$tcl_platform(platform)!="windows"} return + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !win32malloc { + finish_test + return +} + +set testprefix win32heap + +do_test 1.1 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap_size 1048576 + sqlite3_initialize +} {SQLITE_OK} + +do_test 1.2 { + sqlite3 db test.db + catchsql { + CREATE TABLE t1(x); + } +} {0 {}} + +do_test 1.3 { + catchsql { + INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576)); + } +} {1 {out of memory}} + +do_test 1.4 { + catchsql { + SELECT COUNT(*) FROM t1; + } +} {0 0} + +do_test 1.5 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap_size 0 + sqlite3_initialize +} {SQLITE_OK} + +do_test 1.6 { + sqlite3 db test.db + catchsql { + INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576)); + } +} {0 {}} + +do_test 1.7 { + catchsql { + SELECT COUNT(*) FROM t1; + } +} {0 1} + +finish_test diff --git a/test/wordcount.c b/test/wordcount.c index 2161c0717..cf63e983c 100644 --- a/test/wordcount.c +++ b/test/wordcount.c @@ -19,6 +19,7 @@ ** --select Use SELECT mode ** --update Use UPDATE mode ** --delete Use DELETE mode +** --query Use QUERY mode ** --nocase Add the NOCASE collating sequence to the words. ** --trace Enable sqlite3_trace() output. ** --summary Show summary information on the collected data. @@ -28,6 +29,7 @@ ** --commit NNN Commit after every NNN operations ** --nosync Use PRAGMA synchronous=OFF ** --journal MMMM Use PRAGMA journal_mode=MMMM +** --timer Time the operation of this program ** ** Modes: ** @@ -51,11 +53,15 @@ ** Delete mode means: ** (1) DELETE FROM wordcount WHERE word=$new ** -** Note that delete mode is only useful for preexisting databases. The -** wordcount table is created using IF NOT EXISTS so this utility can be -** run multiple times on the same database file. The --without-rowid, -** --nocase, and --pagesize parameters are only effective when creating -** a new database and are harmless no-ops on preexisting databases. +** Query mode means: +** (1) SELECT cnt FROM wordcount WHERE word=$new +** +** Note that delete mode and query mode are only useful for preexisting +** databases. The wordcount table is created using IF NOT EXISTS so this +** utility can be run multiple times on the same database file. The +** --without-rowid, --nocase, and --pagesize parameters are only effective +** when creating a new database and are harmless no-ops on preexisting +** databases. ** ****************************************************************************** ** @@ -75,6 +81,21 @@ #include <stdarg.h> #include "sqlite3.h" +/* Return the current wall-clock time */ +static sqlite3_int64 realTime(void){ + static sqlite3_vfs *clockVfs = 0; + sqlite3_int64 t; + if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ + clockVfs->xCurrentTimeInt64(clockVfs, &t); + }else{ + double r; + clockVfs->xCurrentTime(clockVfs, &r); + t = (sqlite3_int64)(r*86400000.0); + } + return t; +} + /* Print an error message and exit */ static void fatal_error(const char *zMsg, ...){ va_list ap; @@ -95,7 +116,7 @@ static int printResult(void *NotUsed, int nArg, char **azArg, char **azNm){ int i; printf("--"); for(i=0; i<nArg; i++){ - printf(" %s", azArg[i]); + printf(" %s", azArg[i] ? azArg[i] : "(null)"); } printf("\n"); return 0; @@ -170,6 +191,7 @@ static void checksumFinalize(sqlite3_context *context){ #define MODE_SELECT 2 #define MODE_UPDATE 3 #define MODE_DELETE 4 +#define MODE_QUERY 5 int main(int argc, char **argv){ const char *zFileToRead = 0; /* Input file. NULL for stdin */ @@ -180,6 +202,7 @@ int main(int argc, char **argv){ int doTrace = 0; /* True for --trace */ int showStats = 0; /* True for --stats */ int showSummary = 0; /* True for --summary */ + int showTimer = 0; /* True for --timer */ int cacheSize = 0; /* Desired cache size. 0 means default */ int pageSize = 0; /* Desired page size. 0 means default */ int commitInterval = 0; /* How often to commit. 0 means never */ @@ -196,6 +219,8 @@ int main(int argc, char **argv){ FILE *in; /* The open input file */ int rc; /* Return code from an SQLite interface */ int iCur, iHiwtr; /* Statistics values, current and "highwater" */ + sqlite3_int64 sumCnt = 0; /* Sum in QUERY mode */ + sqlite3_int64 startTime; char zInput[2000]; /* A single line of input */ /* Process command-line arguments */ @@ -215,6 +240,8 @@ int main(int argc, char **argv){ iMode = MODE_UPDATE; }else if( strcmp(z,"delete")==0 ){ iMode = MODE_DELETE; + }else if( strcmp(z,"query")==0 ){ + iMode = MODE_QUERY; }else if( strcmp(z,"nocase")==0 ){ useNocase = 1; }else if( strcmp(z,"trace")==0 ){ @@ -225,6 +252,8 @@ int main(int argc, char **argv){ showStats = 1; }else if( strcmp(z,"summary")==0 ){ showSummary = 1; + }else if( strcmp(z,"timer")==0 ){ + showTimer = i; }else if( strcmp(z,"cachesize")==0 && i<argc-1 ){ i++; cacheSize = atoi(argv[i]); @@ -250,6 +279,7 @@ int main(int argc, char **argv){ if( zDbName==0 ){ fatal_error("Usage: %s [--options] DATABASE [INPUTFILE]\n", argv[0]); } + startTime = realTime(); /* Open the database and the input file */ if( sqlite3_open(zDbName, &db) ){ @@ -303,6 +333,13 @@ int main(int argc, char **argv){ sqlite3_free(zSql); /* Prepare SQL statements that will be needed */ + if( iMode==MODE_QUERY ){ + rc = sqlite3_prepare_v2(db, + "SELECT cnt FROM wordcount WHERE word=?1", + -1, &pSelect, 0); + if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n", + sqlite3_errmsg(db)); + } if( iMode==MODE_SELECT ){ rc = sqlite3_prepare_v2(db, "SELECT 1 FROM wordcount WHERE word=?1", @@ -385,6 +422,12 @@ int main(int argc, char **argv){ }else{ fatal_error("SELECT failed: %s\n", sqlite3_errmsg(db)); } + }else if( iMode==MODE_QUERY ){ + sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC); + if( sqlite3_step(pSelect)==SQLITE_ROW ){ + sumCnt += sqlite3_column_int64(pSelect, 0); + } + sqlite3_reset(pSelect); }else{ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pInsert)!=SQLITE_DONE ){ @@ -417,6 +460,25 @@ int main(int argc, char **argv){ sqlite3_finalize(pSelect); sqlite3_finalize(pDelete); + if( iMode==MODE_QUERY ){ + printf("sum of cnt: %lld\n", sumCnt); + rc = sqlite3_prepare_v2(db,"SELECT sum(cnt*cnt) FROM wordcount", -1, + &pSelect, 0); + if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){ + printf("double-check: %lld\n", sqlite3_column_int64(pSelect, 0)); + } + sqlite3_finalize(pSelect); + } + + + if( showTimer ){ + sqlite3_int64 elapseTime = realTime() - startTime; + fprintf(stderr, "%3d.%03d wordcount", (int)(elapseTime/1000), + (int)(elapseTime%1000)); + for(i=1; i<argc; i++) if( i!=showTimer ) fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + if( showSummary ){ sqlite3_create_function(db, "checksum", -1, SQLITE_UTF8, 0, 0, checksumStep, checksumFinalize); @@ -427,7 +489,7 @@ int main(int argc, char **argv){ "SELECT 'avg(cnt): ', avg(cnt) FROM wordcount;\n" "SELECT 'sum(cnt=1):', sum(cnt=1) FROM wordcount;\n" "SELECT 'top 10: ', group_concat(word, ', ') FROM " - "(SELECT word FROM wordcount ORDER BY cnt DESC LIMIT 10);\n" + "(SELECT word FROM wordcount ORDER BY cnt DESC, word LIMIT 10);\n" "SELECT 'checksum: ', checksum(word, cnt) FROM " "(SELECT word, cnt FROM wordcount ORDER BY word);\n" "PRAGMA integrity_check;\n", |